{
	TCL/TK Command Line Implementations of Pascal Routines 
	Copyright (C) 2004-2008 Kevan Hashemi, hashemi@brandeis.edu, Brandeis University
	
	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the
	Free Software Foundation; either version 2 of the License, or (at your
	option) any later version.
	
	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU
	General Public License for more details.
	
	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	59 Temple Place - Suite 330, Boston, MA	02111-1307, USA.
}

program lwdaq;

{
	lwdaq is the interface between our Pascal libraries and TCL/TK. It
	provides init_Lwdaq, which TCL/TK calls when it loads the lwdaq
	dynamic library. init_Lwdaq installs TCL commands, each of which has
	a name beginning with lwdaq in lower-case letters. The lower-case
	letters distinguish these commands from those that we define in
	TCL/TK scripts, which have names that begin with LWDAQ.

	This is a program instead of a unit, even though we compile it into
	a dynamic library. The GPC compiler expects a main program if it is
	to include the _p_initialize routine in the compiled object. We will
	need this routine to be present in the lwdaq.o object when we link
	the final lwdaq.so dynamic library with GCC.

	For a list of routines registered with TCL by this library, scroll
	down to lwdaq_init.

	At the top of each command-line function declaration you will find a
	comment in braces that describes the function. This comment will be
	extracted from lwdaq.pas automatically by our Command Maker script,
	and inserted into an HTML document. The comments appear as they are,
	in the HTML manual, and so include their own HTML tags, and even
	anchors.
}

uses
	utils,images,transforms,image_manip,rasnik,spot,
	tcltk,electronics,bcam,wps,calibration,shadow;

const
	version_num='7.1';
	package_name='lwdaq';

{	
	initialize_pascal starts up the run-time library and calls the
	initialization routines of all units. The routine is in the
	pascal run-time library, libgpc.a.
}
procedure initialize_pascal (argc:integer;argv,envp:pointer);
		external name '_p_initialize';

{
	initialize_main is a procedure provided by the pascal compiler. A
	call to this procedure initializes all the units. 
	
	Ulrich Landgraaf provides the following solution to the change in
	the name of the pascal initialization routine that takes place in the
	2005 release of GPC. In earlier releases of GPC, the routine was
	called _init_pascal_main_program, but later the name changed to
	_p__M0_init, to conform to a two-level naming scheme.
}
{$ifdef __GPC_RELEASE__}
	{$if __GPC_RELEASE__ > 20050000}
		 {$define INIT_CALL '_p__M0_init'}
	{$else}
		 {$define INIT_CALL 'init_pascal_main_program'}
	{$endif}
{$else}
		 {$define INIT_CALL 'init_pascal_main_program'}
{$endif}
procedure initialize_main;
		external name INIT_CALL;

{
	finalize_pascal shuts down the run-time library. We don't use
	it at all in our current code, but we include it here in case
	we use it in some later version. We used it in an early version
	of this shared library.
}
procedure finalize_pascal;
		external name '_p_finalize';
		
{
	The following variables we use to implement the utils gui routines for
	analysis procedures.
}
var
		gui_photo_name:short_string='none';
		gui_zoom:integer=1;
		gui_intensify:short_string='exact';
		gui_text_name:short_string='stdout';
		gui_interp_ptr:pointer=nil;
		gui_wait_ms:integer=-1;
{
	The following long string variable allows us to dispose of a long 
	string before we copy it into the TCL results string. We copy the 
	dynamically-allocated long string into this static variable, dispose
	of the dynamic long string, and then copy the long string out of
	the static variable. If we don't do this, then we run into trouble
	when we turn on our pointer-tracking. The pointer tracking uses
	Tcl_Eval and Tcl_PutMessage to execute print commands in the TCL
	interpreter. These print commands set the TCL results string to 
	an empty string, cancelling our execution of Tcl_SetReturnLongString.
}
var
	lwdaq_long_string:long_string;

{
	lwdaq_gui_draw draws the named image into the TK photo named
	gui_photo_name. The routine calls lwdaq_draw, which, like all the
	lwdaq TclTk commands, clears the global error_string. We save the
	initial value of error_string so we can restore it after the 
	update. This restoration means we can call lwdaq_gui_draw anywhere
	in our code without deleting the existing error_string.
}
procedure lwdaq_gui_draw(s:short_string); 
var c,saved_error_string:short_string;error:integer;
begin
	if (gui_photo_name<>'none') and (gui_interp_ptr<>nil) then begin
		saved_error_string:=error_string;
		c:=' lwdaq_draw '+s+' '+gui_photo_name 
			+' -intensify '+gui_intensify
			+' -zoom '+string_from_integer(gui_zoom,0);
		error:=Tcl_Eval(gui_interp_ptr,c);
		c:='LWDAQ_update';
		error:=Tcl_Eval(gui_interp_ptr,c);
		error_string:=saved_error_string;
	end else
		default_gui_draw(s);
end;

{
	lwdaq_gui_wait pauses for gui_wait_ms milliseconds. If gui_wait_ms
	is -1, the routine opens a window and asks the user to press the
	button before returning.
}
procedure lwdaq_gui_wait(s:short_string); 
var c:short_string;error:integer;
begin
	if (gui_interp_ptr<>nil) then begin
		if (gui_wait_ms>=0) then 
			writestr(c,'LWDAQ_wait_ms ',gui_wait_ms:1)
		else 
			writestr(c,'LWDAQ_button_wait "',s,'"');
		error:=Tcl_Eval(gui_interp_ptr,c);
	end;
end;

{
	lwdaq_gui_support passes control to the graphical user interface to perform
	support for display updates and mouse clicks.
}
procedure lwdaq_gui_support(s:short_string);
var c:short_string;error:integer;
begin
	if (gui_interp_ptr<>nil) then begin
		writestr(c,'LWDAQ_support');
		error:=Tcl_Eval(gui_interp_ptr,c);
	end;
end;

{
	lwdaq_gui_writeln writes a string to a text device using the LWDAQ_print
	routine. The LWDAQ_print routine accepts file names, text widget names, 
	and the names of the standard output (stdout) and standard error (stderr) 
	channels. The name used by lwdaq_gui_writeln is the name stored in the global
	lwdaq_text_name variable.
}
procedure lwdaq_gui_writeln(s:short_string); 
var c:short_string;error:integer;
begin
	c:='LWDAQ_print '+gui_text_name+' "'+s+'"';
	error:=Tcl_Eval(gui_interp_ptr,c);
	if (error<>Tcl_OK) then begin 
		c:='puts stderr "ERROR: Trying to print to '+gui_text_name+'"';
		error:=Tcl_Eval(gui_interp_ptr,c);
	end;
end;

{
<p>lwdaq_config sets global variables that control the operation of the lwdaq analysis libraries. If you specify no options, lwdaq_config returns a string giving you the current values of all the options, <i>except</i> the -eol option. Each option requires a value, which will be assigned to the global variable names in the option. Here are the options and their expected value types. Boolean variables you specify with 0 for false and 1 for true.</p>

<center><table cellspacing=1 border>
<tr><td>-stdout_available</td><td>Boolean</td><td>standard output channel is available</td></tr>
<tr><td>-stdin_available</td><td>Boolean</td><td>standard input channel is available</td></tr>
<tr><td>-track_ptrs</td><td>Boolean</td><td>track memory allocation</td></tr>
<tr><td>-text_name</td><td>String</td><td>text window in which to print messages</td></tr>
<tr><td>-photo_name</td><td>String</td><td>photo in which to draw images and graphs</td></tr>
<tr><td>-zoom</td><td>Integer</td><td>display zoom for images</td></tr>
<tr><td>-intensify</td><td>String</td><td>intensification type for images,<br>
			none, mild, strong, or exact.</td></tr>
<tr><td>-wait_ms</td><td>Integer</td><td>milliseconds to pause during lwdaq_gui_wait</td></tr>
<tr><td>-gamma_correction</td><td>Real</td><td>image drawing gamma correction</td></tr>
<tr><td>-fsr</td><td>Integer</td><td>field size for real numbers returned in strings.</td></tr>
<tr><td>-fsd</td><td>Integer</td><td>decimal places for real numbers returned in strings.</td></tr>
<tr><td>-eol</td><td>String</td><td>end of line characters for text windows and files.</td>
</table></center>

<p>The analysis routines can write to TK text windows, and draw in TK photos through -text_name and -photo_name. During execution, they can pause to allow you to view the intermediate results for -wait_ms millisconds. If you set -wait_ms to -1, TK will open a window with a Continue button in it, which you must click before the analysis proceeds. The <a href="http://www.cgsd.com/papers/gamma.html">gamma correction</a></td> sets the gray scale image display gamma correction used by lwdaq_draw.</p>

<p>Many routines return real numbers in strings. These real numbers will have a fixed number of decimal places equal to the global Pascal variable <i>fsd</i> and a total field size equal to the global Pascal variable <i>fsr</i>.</p> 
}
function lwdaq_config(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	arg_index:integer;
	vp:pointer;
	
begin
	error_string:='';
	lwdaq_config:=Tcl_Error;

	if (not odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_config ?option value?".');
		exit;
	end;
	
	if argc=1 then begin
		Tcl_SetReturnShortString(interp,
			' -stdout_available '+string_from_boolean(stdout_available)
			+' -stdin_available '+string_from_boolean(stdin_available)
			+' -track_ptrs '+string_from_boolean(track_ptrs)
			+' -text_name '+gui_text_name
			+' -photo_name '+gui_photo_name
			+' -zoom '+string_from_integer(gui_zoom,0)
			+' -intensify '+gui_intensify
			+' -wait_ms '+string_from_integer(gui_wait_ms,0)
			+' -gamma_correction '+string_from_real(gamma_correction,0,1)
			+' -fsr '+string_from_integer(fsr,0)
			+' -fsd '+string_from_integer(fsd,0));
	end else begin
			arg_index:=1;
			while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-stdout_available') then stdout_available:=Tcl_ObjBoolean(vp)
		else if (option='-stdin_available') then stdin_available:=Tcl_ObjBoolean(vp)
		else if (option='-track_ptrs') then track_ptrs:=Tcl_ObjBoolean(vp)
		else if (option='-text_name') then gui_text_name:=Tcl_ObjShortString(vp)
		else if (option='-photo_name') then gui_photo_name:=Tcl_ObjShortString(vp)
		else if (option='-zoom') then gui_zoom:=Tcl_ObjInteger(vp)
		else if (option='-intensify') then gui_intensify:=Tcl_ObjShortString(vp)
		else if (option='-wait_ms') then gui_wait_ms:=Tcl_ObjInteger(vp)
		else if (option='-gamma_correction') then gamma_correction:=Tcl_ObjReal(vp)
		else if (option='-fsr') then fsr:=Tcl_ObjInteger(vp)
		else if (option='-fsd') then fsd:=Tcl_ObjInteger(vp)
		else if (option='-eol') then eol:=Tcl_ObjShortString(vp)
		else begin 
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-stdout_available ? -stdin_available ?'
				+' -track_ptrs ? -text_name ? -photo_name ? -wait_ms ?'
				+' -gamma_correction -fsr -fsd -eol ? -zoom ? -intensify ?".'); 
			exit;
		end;
			end;
	end;

	if error_string <> '' then Tcl_SetReturnShortString(interp,error_string);
	lwdaq_config:=Tcl_OK;	
end;

{
<p>lwdaq_image_create creates a new image and returns a unique name for the image, by which the interpreter can identify the image to other lwdaq routines.</p>

<table border cellspacing=2>
<tr><th>Option</th><th>Function</th></tr>
<tr><td>-name</td><td>Specify the name for the image.</td></tr>
<tr><td>-results</td><td>Set the image results string.<td></td></tr>
<tr><td>-width</td><td>The width of the image in pixels.</td></tr>
<tr><td>-height</td><td>The height of the image in pixels</td></tr>
<tr><td>-data</td><td>Pixel intensity values as a binary array of bytes.</td></tr>
<tr><td>-left</td><td>Left column of analysis bounds.</td></tr>
<tr><td>-right</td><td>Right column of analysis bounds.</td></tr>
<tr><td>-top</td><td>Topm row of analysis bounds.</td></tr>
<tr><td>-bottom</td><td>Bottom row of analysis bounds.</td></tr>
<tr><td>-try_header</td><td>Try the image data for a legitimate lwdaq-format header.</td></tr>
</table>

<p>The above table lists the options accepted by lwdaq_image_create, and their functions. If you use the -name option and provide the name of a pre-existing image in the lwdaq image list, lwdaq_image_create deletes the pre-existing image. If you specify "-data $value", the routine copies $value into the image's intensity array, starting at the first pixel of the first row. When you combine "-data $value" with "-try_header 1", the routine looks at the first bytes in $value to see if it contains a valid image header, specifying image width and height, as well as analysis bounds and a results string. When the routine looks for the header, it assumes that the bytes in the header specify two-byte integers in big-endian order.</p>

<p>If you have -try_header 0, or if the routine's effort to find a header fails, lwdaq_image_create will look at the values you specify for the analysis bounds with the -left, -top, -right, and -bottom options. A value of &minus;1 directs the routine to place the boundary at the edge of the image. The default values for these options are all &minus;1.</p>
}
function lwdaq_image_create(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	arg_index:integer;
	width,height,data_size,copy_size:integer=-1;
	left,right,top,bottom:integer=-1;
	try_header:boolean=false;
	ihp:image_header_ptr_type;
	data_obj,data_ptr:pointer=nil;
	name:short_string='';
	results:short_string='';
	ip:image_ptr_type=nil;
	vp:pointer=nil;
	char_index:integer;
	q:integer;

begin
	error_string:='';
	lwdaq_image_create:=Tcl_Error;

	if (argc<3) or (not odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_image_create option value ?option value?".');
		exit;
	end;
	
	arg_index:=1;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-name') then name:=Tcl_ObjShortString(vp)
		else if (option='-results') then results:=Tcl_ObjShortString(vp)
		else if (option='-width') then width:=Tcl_ObjInteger(vp)
		else if (option='-height') then height:=Tcl_ObjInteger(vp)
		else if (option='-data') then data_obj:=vp
		else if (option='-left') then left:=Tcl_ObjInteger(vp)
		else if (option='-right') then right:=Tcl_ObjInteger(vp)
		else if (option='-top') then top:=Tcl_ObjInteger(vp)
		else if (option='-bottom') then bottom:=Tcl_ObjInteger(vp)
		else if (option='-try_header') then try_header:=Tcl_ObjBoolean(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-name -width -height -data -left -top'
				+' -bottom -right -results -try_header".');
			exit;
		end;
	end;
	
	if data_obj<>nil then begin
		data_ptr:=Tcl_GetByteArrayFromObj(data_obj,data_size);
		if data_size<0 then begin
			Tcl_SetReturnShortString(interp,'Data size less than zero.');
			exit;
		end;
		
		ihp:=pointer(data_ptr);
		if try_header then begin
			q:=local_from_big_endian_shortint(ihp^.j_max)+1;
			if (q>0) then height:=q;
			q:=local_from_big_endian_shortint(ihp^.i_max)+1;
			if (q>0) then width:=q;
		end;

		if (width<=0) and (height<=0) then begin
			width:=trunc(sqrt(data_size));
			if sqr(width)<data_size then width:=width+1;
			height:=width;
		end;

		if (width<=0) and (height>0) then begin
			width:=trunc(data_size/height);
			if width*height<data_size then width:=width+1;
		end;

		if (width>0) and (height<=0) then begin
			height:=trunc(data_size/width);
			if width*height<data_size then height:=height+1;
		end;

		if (width*height>data_size) then copy_size:=data_size
		else copy_size:=(width*height);
	end;
	
	if (data_obj=nil) and try_header then begin
		Tcl_SetReturnShortString(interp,'Specified -try_header 1 without -data $value.');
		exit;
	end;
	
	ip:=new_image(height,width);
	if ip=nil then begin 
		Tcl_SetReturnShortString(interp,'Failed to allocate memory for new image.');
		exit;
	end;
	
	if data_ptr<>nil then begin
		block_move(data_ptr,@ip^.intensity,copy_size);
	end;
	
	if try_header then begin
		q:=local_from_big_endian_shortint(ihp^.left);
		if (q>=0) then left:=q;
	end;
	if (left<0) or (left>=width) then left:=0;
	ip^.analysis_bounds.left:=left;
	
	if try_header then begin
		q:=local_from_big_endian_shortint(ihp^.right);
		if (q>left) then right:=q;
	end;
	if (right<=left) or (right>=width) then right:=width-1;
	ip^.analysis_bounds.right:=right;

	if try_header then begin
		q:=local_from_big_endian_shortint(ihp^.top);
		if (q>=0) then top:=q;
	end;
	if (top<1) or (top>=height) then top:=1;
	ip^.analysis_bounds.top:=top;
	
	if try_header then begin
		q:=local_from_big_endian_shortint(ihp^.bottom);
		if (q>top) then bottom:=q;
	end;
	if (bottom<=top) or (bottom>=height) then bottom:=height-1;
	ip^.analysis_bounds.bottom:=bottom;
	
	ip^.results:=results;
	if try_header and (ip^.results='') then begin
		char_index:=0;
		while (char_index<short_string_length) 
				and (ihp^.results[char_index]<>chr(0)) do begin
			ip^.results:=ip^.results+ihp^.results[char_index];
			inc(char_index);
		end;
	end;
	
	if name<>'' then begin
		while valid_image_name(name) do
			dispose_image(image_ptr_from_name(name));
		ip^.name:=name;
	end;
	
	if error_string='' then Tcl_SetReturnShortString(interp,ip^.name)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_image_create:=Tcl_OK;
end;

{
<p>lwdaq_draw transfers the named image into the named TK photo. You pass the lwdaq image name followed by the tk photo name, and then your options in the form ?option value?. When the routine draws the image, it over-writes the first few pixels in the first image row with a header block containing the image dimensions, its analysis bounds, and its results string.</p>

<p>The -intensify option can take four values: mild, strong, exact, and none. Mild intensification displays anything darker than four standard deviations below the mean intensity as black, and anything brighter than four standard deviations above the mean intensity as white. In between black and white the display is linear with pixel brightness. Strong intensification does the same thing, but for a range of two standard deviations from the mean. Exact displays the darkest spot in the image as black and the brightest as white. In all three cases, we calculate the mean, standard deviation, minimum, and maximum intensity of the image within the <i>analysis bounds</i>, not across the entire image.</p>

<p>The -zoom option scales the image as we draw it in the TK photo. If the TK photo is initially smaller than the size required by the zoomed image, the TK photo will expand to accommodate the zoomed image. But if the TK photo is initially larger than required, the TK photo will not contract to the smaller size of the zoomed image. The -zoom option can take any value between 0.1 and 10. But the effective value of -zoom is dicated by the requirements of sub-sampling. If -zoom is greater than 1, we round it to the nearest integer, <i>e</i>, and draw each image pixel on the screen as a block of <i>e</i>&times;<i>e</i> pixels. If -zoom is less than 1, we round its inverse to the nearest integer, <i>c</i>. We draw only one pixel out of every <i>c</i> pixels in the TK photo. If -zoom = 0.3, we draw every third pixel. If -zoom = 0.4, we draw every third pixel if your computer rounds 1/0.4 to 3, or every second pixel if your computer rounds 1/0.4 to 2.</p>

<p>With -clear set to 1, lwdaq_draw clears the overlay in the lwdaq image before drawing in the TK photo. The overlay may contain a graph or oscilloscope display, or analysis indicator lines. If you don't want these to be displayed, set -clear to 1. But note that whatever was in the overlay will be lost.</p>

<p>By default, -show_bounds is 1, and the routine draws a blue rectangle to show the the image analysis boundaries, which are used by image analysis routines like lwdaq_rasnik and lwdaq_bcam. But with -show_bounds set to 0, this blue rectangle is not drawn. If you want to be sure that you don't have a blue rectangle drawn over your gray-scale image, you should also specify -clear 1, so that lwdaq_draw will clear the image overlay of any pre-existing blue rectangles.</p>
}
function lwdaq_draw(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

const
	min_zoom=0.1;
	max_zoom=10;
	
var 
	option,value:short_string;
	arg_index,char_index:integer;
	width,height,data_size:integer=0;
	image_name,photo_name,intensify:short_string='';
	ip:image_ptr_type=nil;
	zoom:real=1;
	vp,ph:pointer=nil;
	pib:Tk_PhotoImageBlock;
	subsampleX,subsampleY,zoomX,zoomY:integer;
	draw_width,draw_height:integer;
	clear:boolean=false;
	show_bounds:boolean=true;
	saved_bounds:ij_rectangle_type;

begin
	error_string:='';
	lwdaq_draw:=Tcl_Error;
		
	if (argc<3)	or (not odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_draw image photo ?option value?".');
		exit;
	end;
		
	image_name:=Tcl_ObjShortString(argv[1]);
	photo_name:=Tcl_ObjShortString(argv[2]);
	arg_index:=3;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-intensify') then intensify:=Tcl_ObjShortString(vp)
		else if (option='-zoom') then zoom:=Tcl_ObjReal(vp)
		else if (option='-clear') then clear:=Tcl_ObjBoolean(vp)
		else if (option='-show_bounds') then show_bounds:=Tcl_ObjBoolean(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-intensify -zoom -clear -show_bounds".');
			exit;
		end;
	end;
	
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	embed_image_header(ip);

	if intensify='exact' then ip^.intensification:=exact_intensify
	else if intensify='mild' then ip^.intensification:=mild_intensify
	else if intensify='strong' then ip^.intensification:=strong_intensify
	else ip^.intensification:=no_intensify;
	
	ph:=Tk_FindPhoto(interp,photo_name);
	if ph=nil then begin
		Tcl_SetReturnShortString(interp,'Photo "'+photo_name+'" does not exist.');
		exit;
	end;
	Tk_PhotoBlank(ph);
	
	if clear then clear_overlay(ip);
	if show_bounds then
		draw_overlay_rectangle(ip,ip^.analysis_bounds,blue_color);
	draw_image(ip);
	with pib do begin
		pixelptr:=@drawing_space_ptr^[0];
		width:=ip^.i_size;
		height:=ip^.j_size;
		pitch:=width*sizeof(drawing_space_pixel_type);
		pixelSize:=sizeof(drawing_space_pixel_type);
		offset[red]:=0;
		offset[green]:=offset[red]+sizeof(byte);
		offset[blue]:=offset[green]+sizeof(byte);
		offset[alpha]:=offset[blue]+sizeof(byte);
	end;
	if zoom<min_zoom then zoom:=min_zoom;
	if zoom>max_zoom then zoom:=max_zoom;
	if zoom>=1 then begin
		subsampleX:=1;
		subsampleY:=1;
		zoomX:=round(zoom);
		zoomY:=round(zoom);
		draw_width:=pib.width*zoomX;
		draw_height:=pib.height*zoomY;
	end else begin
		subsampleX:=round(1/zoom);
		subsampleY:=round(1/zoom);
		zoomX:=1;
		zoomY:=1;
		draw_width:=round(pib.width/subsampleX);
		draw_height:=round(pib.height/subsampleY);
	end;
{$ifdef TCLTK_8_5}
	Tk_PhotoPutZoomedBlock(interp,ph,@pib,0,0,
		draw_width,draw_height,
		zoomX,zoomY,subsampleX,subsampleY,1);
{$else}
	Tk_PhotoPutZoomedBlock(ph,@pib,0,0,
		draw_width,draw_height,
		zoomX,zoomY,subsampleX,subsampleY,1);
{$endif}
	
	if error_string<>'' then Tcl_SetReturnShortString(interp,error_string);
	lwdaq_draw:=Tcl_OK;
end;

{
<p>lwdaq_image_destroy disposes of an image. You can specify multiple images, or image name patterns with * and ? wild cards. You can enter multiple image names on the command line, too.</p>
}
function lwdaq_image_destroy(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	arg_index:integer;
	image_name:short_string='';
	vp:pointer;	

begin
	error_string:='';
	lwdaq_image_destroy:=Tcl_Error;
	
	if (argc<2) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_image_destroy image".');
		exit;
	end;
	
	for arg_index:=1 to argc-1 do begin
	image_name:=Tcl_ObjShortString(argv[arg_index]);
		dispose_named_images(image_name);
	end;
	
	if error_string<>'' then Tcl_SetReturnShortString(interp,error_string);
	lwdaq_image_destroy:=Tcl_OK;
end;

{
<p>lwdaq_image_contents returns a byte array containing the intensity array from the named image. In the first line of the image the routine records the image dimensions, analysis boundry, and results string. The integers are two-bytes long, and we use big-endian byte ordering, so the high-order byte is first.</p>

<p>If you specify -truncate 1, the routine removes all trailing zero-bytes from the data. When we create a new image to accomodate the same data later, we clear the image intensity array before we copy in the new data, so the image is re-constructed faithfully. This truncation is effective at reducing the size of data files from instruments that don't fill the intensity array with real data, but instead use the intensity array as a place to store one-dimensional data, and use the overlay as a white-board upon which to render the data (like the Voltmeter). If you specify -data_only 1, the routine chops off the leading row of data, leaving only the data from the first pixel of the first row onwards, which is the block of data operated upon by our lwdaq_data_manipulate routines. If you specify -record_size larger than 1, the routine makes sure that the size of the block it returns is divisible by the record size.</p>
}
function lwdaq_image_contents(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	arg_index:integer;
	image_name:short_string='';
	ip,cip:image_ptr_type;
	vp:pointer;	
	char_index,i,j,ci,cj:integer;
	truncate,data_only:boolean=false;
	copy_size:integer=0;
	record_size:integer=1;
	cp:pointer;

begin
	error_string:='';
	lwdaq_image_contents:=Tcl_Error;
	
	if (argc<2) then begin
			Tcl_SetReturnShortString(interp,
				'Wrong number of arguments, must be "lwdaq_image_contents image".');
		 exit;
	end;

	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;

	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-truncate') then truncate:=Tcl_ObjBoolean(vp)
		else if (option='-data_only') then data_only:=Tcl_ObjBoolean(vp)
		else if (option='-record_size') then record_size:=Tcl_ObjInteger(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-truncate -data_only -record_size".');
			exit;
		end;
	end;

	embed_image_header(ip);
	
	if truncate then begin
		with ip^ do begin
			j:=j_size-1;
			i:=i_size-1;
			copy_size:=sizeof(ip^.intensity);
			while (j>0) and (intensity[j,i]=0) do begin
				if i=0 then begin
					dec(j);
					i:=i_size-1;
				end else begin
					dec(i);
				end;
				dec(copy_size);
			end;
		end;
	end
	else copy_size:=sizeof(ip^.intensity);
	
	if data_only then begin
		copy_size:=copy_size-ip^.i_size;
		cp:=@ip^.intensity[1,0];
	end else begin
		cp:=@ip^.intensity[0,0];
	end;
	
	if record_size>1 then
		if (copy_size mod record_size) > 0 then
			copy_size:=copy_size+record_size-(copy_size mod record_size);
	
	if error_string='' then Tcl_SetReturnByteArray(interp,cp,copy_size)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_image_contents:=Tcl_OK;
end;

{
<p>lwdaq_photo_contents returns a byte array containing gray-scale intensity array corresponding to a tk photo. The routine uses the red intensity as the gray-scale intensity, which will work in a purely gray-scale image, and assumes that the red intensity is an 8-bit number.<p>

<p>The routine embeds the image dimensions in the first four pixels of the image by over-writing them with j_size-1 and i_size-1 each as two-byte integers in big-endian format. If the image is one that has been previously stored or drawn by lwdaq routines, the first twelve pixels of the first line will already contain the image dimensions, plus the analysis boundaries, all encoded as two-byte big-endian integers. Because the routine already knows for sure what the image dimensions are, it over-writes dimensions in the first row. But it does not over-write the analysis boundaries. These may be correct or incorrect. You can pass this routine's result to lwdaq_image_create, and have the image-creating routine check the first twelve bytes for valid analysis bounds, or ignore these bounds and use newly-specified bounds.</p>

<p>To assemble the 8-bit gray-scale image, the routine uses the lwdaq scratch image. If the routine were to allocate and dispose of an image, the printing activity of the disposal when -track_ptrs is set to 1 would alter the TCL return string.</p>
}
function lwdaq_photo_contents(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	arg_index:integer;
	photo_name:short_string='';
	ip:image_ptr_type;
	vp,ph:pointer=nil;	
	ihp:image_header_ptr_type=nil;
	char_index:integer;
	pib:Tk_PhotoImageBlock;
	i,j,r:integer=0;
	pp:^intensity_pixel_type;
	
begin
	error_string:='';
	lwdaq_photo_contents:=Tcl_Error;
	
	if (argc<2) then begin
			Tcl_SetReturnShortString(interp,
				'Wrong number of arguments, must be "lwdaq_photo_contents photo".');
		exit;
	end;
		
	photo_name:=Tcl_ObjShortString(argv[1]);
	ph:=Tk_FindPhoto(interp,photo_name);
	if ph=nil then begin
		Tcl_SetReturnShortString(interp,'Photo "'+photo_name+'" does not exist.');
		exit;
	end;
	r:=Tk_PhotoGetImage(ph,@pib);
	with pib do begin
		dispose_named_images(scratch_image_name);
		ip:=new_image(height,width);
		ip^.name:=scratch_image_name;
		pp:=pointer(pixelptr);
		for j:=0 to height-1 do begin
			for i:=0 to width-1 do begin
				ip^.intensity[j,i]:=pp^;
				pp:=pointer(cardinal(pp)+pixelSize);
			end;
		end;
	end;
	with ip^ do begin
		ihp:=pointer(@intensity);
		ihp^.i_max:=big_endian_from_local_shortint(i_size-1);
		ihp^.j_max:=big_endian_from_local_shortint(j_size-1);
	end;
	
	if error_string='' then 
		Tcl_SetReturnByteArray(interp,@ip^.intensity,sizeof(ip^.intensity))
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_photo_contents:=Tcl_OK;
end;

{
<p>lwdaq_image_characteristics returns features of the image: the left, top, right, and bottom edges of the analysis boundries, the average, standard deviation, maximum, and minimum values of intensity, and the height and width of the image.</p>
}
function lwdaq_image_characteristics(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	image_name,result:short_string='';
	ip:image_ptr_type;
	vp:pointer;	

begin
	error_string:='';
	lwdaq_image_characteristics:=Tcl_Error;
	
	if (argc<2) then begin
			Tcl_SetReturnShortString(interp,
				'Wrong number of arguments, must be "lwdaq_image_characteristics image".');
			exit;
	end;
		
	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	with ip^.analysis_bounds do
		writestr(result,left:1,' ',top:1,' ',right:1,' ',bottom:1,' ',
			image_average(ip):3:1,' ',image_amplitude(ip):3:1,' ',
			image_maximum(ip):3:1,' ',image_minimum(ip):3:1,' ',
			ip^.j_size:1,' ',ip^.i_size:1);

	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_image_characteristics:=Tcl_OK;
end;

{
<p>lwdaq_image_histogram returns a histogram of image intensity within the analysis bounds of an image. The histogram takes the form of an x-y graph in a space-delimited string, with the x-coordinate representing intensity, and the y-coordinate representing frequency.</p>
}
function lwdaq_image_histogram(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	image_name:short_string='';
	lsp:long_string_ptr;
	hp:xy_graph_ptr_type;
	ip:image_ptr_type;
	vp:pointer;	
	i:integer;

begin
	error_string:='';
	lwdaq_image_histogram:=Tcl_Error;
	
	if (argc<2) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_image_histogram image".');
		exit;
	end;
	
	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	hp:=image_histogram(ip);
	lsp:=new_long_string;
	lsp^:='';
	for i:=0 to hp^.num_points-1 do
		writestr(lsp^,lsp^,hp^[i].x:1:0,' ',hp^[i].y:1:0,' ');
	dispose_xy_graph(hp);
	lwdaq_long_string:=lsp^;
	dispose_long_string(lsp);
	
	if error_string='' then Tcl_SetReturnLongString(interp,lwdaq_long_string)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_image_histogram:=Tcl_OK;
end;

{
<p>lwdaq_image_exists returns a list of images in the lwdaq image list that match the image_name pattern you pass to the routine. If you pass "*", it will return a list of all existing images. If there are no matching images, lwdaq_image_exists returns an empty string.</p>
}
function lwdaq_image_exists(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	result:short_string='';
	arg_index:integer;
	image_name:short_string='*';
	ip:image_ptr_type;
	vp:pointer;	
	verbose:boolean=false;

begin
	error_string:='';
	lwdaq_image_exists:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_image_exists image ?option value?".');
		exit;
	end;
	
	image_name:=Tcl_ObjShortString(argv[1]);

	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-verbose') then verbose:=Tcl_ObjBoolean(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-verbose".');
			exit;
		end;
	end;

	write_image_list(result,image_name,verbose);
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_image_exists:=Tcl_OK;
end;

{
<p>lwdaq_image_results returns an image's results string, which may be up to short_string_length characters long.</p>
}
function lwdaq_image_results(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	arg_index:integer;
	image_name:short_string='';
	ip:image_ptr_type;
	vp:pointer;	

begin
	error_string:='';
	lwdaq_image_results:=Tcl_Error;
	
	if (argc<2) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_image_results image".');
		exit;
	end;
	
	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	if error_string='' then Tcl_SetReturnShortString(interp,ip^.results)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_image_results:=Tcl_OK;
end;

{
<p>lwdaq_image_manipulate returns the name of a new image derived from one or more images passed to lwdaq_image_manipulate. If we set the -replace option to 1 then lwdaq_image_manipulate replaces the original image with the result image. The command takes the name of an image in the LWDAQ image list, and the name of a manipulation to be performed upon this image. The currently-supported manipulations are as follows.</p>

<table border cellspacing=2>
<tr><th>Manipulation<br>Code</th><th>Function</th></tr>
<tr><td>none</td><td>No manipulation of pixels, the new image is the old image.</td></tr>
<tr><td>invert</td><td>Turn image upside-down by reversing order of pixels. Top-left becomes bottom-right.</td></tr>
<tr><td>reverse_rows</td><td>Reverse the order of the rows. The top row becomes the bottom row.</td></tr>
<tr><td>soec</td><td>Swap odd and even columns. This routine corrects errors in images recorded from certain obsolete data acquisition systems.</td></tr>
<tr><td>grad_i</td><td>Magnitude of the horizontal intensity derivative.</td></tr>
<tr><td>grad_i_s</td><td>Horizontal intensity derivative, signed.</td></tr>
<tr><td>grad_j</td><td>Magnitude of the vertical intensity derivative.</td></tr>
<tr><td>grad_j_s</td><td>Vertical intensity derivative, signed.</td></tr>
<tr><td>grad</td><td>Magnitude of the intensity gradient.</td></tr>
<tr><td>negate</td><td>Negate the image.</td></tr>
<tr><td>smooth</td><td>Smooth with 3 &times; 3 box filter.</td></tr>
<tr><td>copy</td><td>Copy the image into a new image.</td></tr>
<tr><td>combine</td><td>Replaces a portion of the image.</td></tr>
<tr><td>subtract</td><td>Subtract a second image from the first image.</td></tr>
</table>
<small><b>Table:</b> Manipulation Codes and their Functions.</small>

<p>The <i>subtract</i> manipulation requires you to name a second image, which will be subtracted from the first to create a third image. The <i>none</i> manipulation does nothing. In each case, we have a new image pointer, but with the <i>none</i> manipulation, this image pointer is a pointer to the first image. The <i>combine</i> manipulation allows you to write over the data in an image, starting with the <i>offset</i>'th pixel. You specify <i>offset</i> after the data. The manipulation copies the entire contents of an <i>m</i>-byte binary block into the image, starting at pixel <i>offset</i>, and ending at pixel <i>offset+m-1</i>. If the copy goes past the end of the image array, the manipulation aborts without doing anything, and returns an error.</p>

<p>The gradient manipulations either return an absolute intensity gradient or a signed intensity gradient. We calculate the horizontal gradient at pixel (i,j) by subtracting the intensity of pixel (i-1,j) from that of pixel (i+1,j). The vertical gradient is (i,j+1) minus (i,j-1). When we return the magnitude of the gradient, the intensity of the gradient image is simply the absolute value of the gradient. When we return the signed gradient, we offset the gradient image intensity by mid_intensity, which is 128 for eight-bit gray scale images. Thus an intensity of 128 mean zero gradient, and an intensity of 138 means +10. When the gradient exceeds 127 or -128, we clip its value to 255 and 0 respectively. For more details, see the image_filter and subsequent routine in <a href="../../Software/Sources/image_manip.pas">image_manip.pas</a>.</p>

<table border cellspacing=2>
<tr><th>Option</th><th>Function</th></tr>
<tr><td>-name</td><td>The name of the new image will be $value.</td></tr>
<tr><td>-results</td><td>Set the new image results string to $value.</td></tr>
<tr><td>-replace</td><td>If $value is 1, delete the original image and replace it with the new one. 0 by default.</td></tr>
<tr><td>-clear</td><td>if $value is 1, clear overlay of final image, 0 by default.</td></tr>
<tr><td>-fill</td><td>if $value is 1, fill overlay of final image with white, 0 by default.</td></tr>
<tr><td>-paint</td><td>paint the analysis bounds with color number $value.</td></tr>
<tr><td>-bottom</td><td>Set the bottom of the analysis bounds to $value.</td></tr>
<tr><td>-top</td><td>Set the top of the analysis bounds to $value.</td></tr>
<tr><td>-left</td><td>Set the left of the analysis bounds to $value.</td></tr>
<tr><td>-right</td><td>Set the rigth of the analysis bounds to $value.</td></tr>
</table>

<p>With -name you specify the name of the new image created by the manipulation, or the existing image if there is no new image created by the manipulation. Any pre-existing images with this name will be destroyed before the name change occurs.</p>

<p>With -replace 0, the manipulation creates a new image and returns its name. With -replace 1, the manipulation over-writes data in the old image and returns the old image name.</p>

<p>The -paint option instructs lwdaq_image_manipulate to paint the entire area within the analysis bounds with the color given by $value. This value should be a number between 0 and 255. The value 0 is for transparant. Other than the 0-value, the number will be treated like an eight-bit RGB code, with the top three bits for red, the middle three for green, and the bottom three for blue. Thus $E0 (hex E0) is red, $1C is green, and $03 is blue.</p>

<p>In addition to the pixel manipulations, we also have options to change other secondary properties of the image. The table above shows the available manipulation options, each of which is followed by a value in the command line, in the format ?option value?.</p>

<p>When you specify the analysis bounds, a value of &minus;1 is the code for "do nothing". The boundary will remain as it was. This use of the &minus;1 code contasts with that of lwdaq_image_create, where &minus;1 directs lwdaq_image_create to move the boundary to the edge of the image.</p>
}
function lwdaq_image_manipulate(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	arg_index:integer;
	image_name,second_image_name,name,result:short_string='';
	manipulation:short_string='none';
	results:short_string=null_code;
	left,right,top,bottom,paint:integer=-1;
	replace:boolean=false;
	clear,fill:boolean=false;
	ip,nip,ip_2:image_ptr_type;
	data_obj,data_ptr:pointer=nil;
	vp:pointer;	
	data_size,offset:integer=-1;

begin
	error_string:='';
	lwdaq_image_manipulate:=Tcl_Error;
{
	This routine needs at least three arguments: the routine name, the image name, and
	the manipulation name.
}
	if (argc<3) then begin
		Tcl_SetReturnShortString(interp,'Wrong number of arguments, must be '
			+'"lwdaq_image_manipulate image_name manipulation ?option value?".');
		exit;
	end;
{
	Get the image name and manipulation name.
}
	arg_index:=1;
	image_name:=Tcl_ObjShortString(argv[arg_index]);
	inc(arg_index);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	manipulation:=Tcl_ObjShortString(argv[arg_index]);
	inc(arg_index);
{
	Perform the specified manipulation.
}
	if manipulation='copy' then nip:=image_copy(ip)
	else if manipulation='grad_i' then nip:=image_grad_i(ip)
	else if manipulation='grad_i_s' then nip:=image_filter(ip,-1,0,1,0,1,0,1)
	else if manipulation='grad_j' then nip:=image_grad_j(ip)
	else if manipulation='grad_j_s' then nip:=image_filter(ip,0,1,0,-1,0,1,1)
	else if manipulation='grad' then nip:=image_grad(ip)
	else if manipulation='smooth' then nip:=image_filter(ip,1,1,1,1,1,1,9)
	else if manipulation='negate' then nip:=image_negate(ip)
	else if manipulation='invert' then nip:=image_invert(ip)
	else if manipulation='reverse_rows' then nip:=image_reverse_rows(ip)
	else if manipulation='soec' then nip:=image_soec(ip)
	else if manipulation='subtract' then begin
		if argc<arg_index+1 then begin
			Tcl_SetReturnShortString(interp,'Specify second image name.');
			exit;
		end;
		second_image_name:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		ip_2:=image_ptr_from_name(second_image_name);
		if not valid_image_ptr(ip_2) then begin
			Tcl_SetReturnShortString(interp,'Image "'+second_image_name+'" does not exist.');
			exit;
		end;
		nip:=image_subtract(ip,ip_2);
	end else if manipulation='none' then begin 
		nip:=ip;
	end else begin
		Tcl_SetReturnShortString(interp,'Bad manipulation "'+manipulation
			+'", must be one of "copy subtract negate '
			+'invert reverse_rows soec grad_i grad_i_s grad_j '
			+ 'grad_j_s grad smooth none".');
		exit;
	end;
{
	Scan the command arguments for option specifiers and record their values.
	If we encounter an invalid argument beginning with a hyphen, we report
	an error.
}
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-name') then name:=Tcl_ObjShortString(vp)
		else if (option='-results') then results:=Tcl_ObjShortString(vp)
		else if (option='-replace') then replace:=Tcl_ObjBoolean(vp)
		else if (option='-bottom') then bottom:=Tcl_ObjInteger(vp)
		else if (option='-top') then top:=Tcl_ObjInteger(vp)
		else if (option='-left') then left:=Tcl_ObjInteger(vp)
		else if (option='-right') then right:=Tcl_ObjInteger(vp)
		else if (option='-clear') then clear:=Tcl_ObjBoolean(vp)
		else if (option='-fill') then fill:=Tcl_ObjBoolean(vp)
		else if (option='-paint') then paint:=Tcl_ObjInteger(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-name -results -replace -bottom -top -left -right -clear -paint".');
			exit;
		end;
	end;
{
	Perform the option modifications to the new image.
}
	if replace and (nip<>ip) then begin
		nip^.name:=ip^.name;
		dispose_image(ip);
	end;
	if results<>null_code then nip^.results:=results;
	if left<>-1 then begin
		if (left>0) and (left<nip^.i_size) then
			nip^.analysis_bounds.left:=left
		else
			nip^.analysis_bounds.left:=0;
	end;
	if right<>-1 then begin
		if (right>left) and (right<nip^.i_size) then
			nip^.analysis_bounds.right:=right
		else 
			nip^.analysis_bounds.right:=nip^.i_size-1;
	end;
	if top<>-1 then begin
		if (top>1) and (top<nip^.j_size) then
			nip^.analysis_bounds.top:=top
		else
			nip^.analysis_bounds.top:=1;
	end;
	if bottom<>-1 then begin
		if (bottom>top) and (bottom<nip^.j_size) then
			nip^.analysis_bounds.bottom:=bottom
		else
			nip^.analysis_bounds.bottom:=nip^.j_size-1;
	end;
	if name<>'' then begin
		while valid_image_name(name) do
			dispose_image(image_ptr_from_name(name));
		nip^.name:=name;
	end;
	if clear then clear_overlay(nip);
	if fill then fill_overlay(nip);
	if paint>=0 then paint_overlay_bounds(nip,paint);
{
	If we encountered no errors, teturn the name of the new image.
	Otherwise return the error message;
}
	if error_string='' then Tcl_SetReturnShortString(interp,nip^.name)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_image_manipulate:=Tcl_OK;
end;

{
<p>lwdaq_data_manipulate operates upon the data in an image, and we intend it for use with instruments that store one-dimensional arrays of data in an image's intensity array. Our convention, when using the intensity array in this way, is to start storing data in the first column of the second row. This leaves the first row free for header information when we store the image to disk. We refer to the block of memory starting with the first byte of the second row, and ending with the last byte of the last row, as the <i>data space</i>. We specify bytes in the data space with their <i>byte address</i>, which is zero at the first byte in the data space. The routine does not return a text string. It either returns an error or an empty string. The data manipulations alter the existing image.</p>

<table border cellspacing=2>
<tr><th>Manipulation</th><th>Function</th></tr>
<tr><td>write</td><td>Writes a block of data into the data space.</td></tr>
<tr><td>read</td><td>Reads a block of data from the data space.</td></tr>
<tr><td>shift</td><td>Shifts data towards start of data space.</td></tr>
<tr><td>clear</td><td>Clears the data.</td></tr>
<tr><td>none</td><td>No action.</td></tr>
</table>

<p>The <i>write</i> function requires two parameters: the data you wish to write to the data space, and the byte address at which you want the first byte of your data to be written. The following command writes the contents of <i>data</i> to the data space of the image named <i>image_name</i> starting at the first byte in the data space (which is the first pixel in the second row).</p>

<pre><small>lwdaq_data_manipulate image_name write 0 $data</small></pre>

<p>The <i>read</i> function requires two parameters: the number of bytes you wish to read from the data space, and the byte address at which you want to start reading. The following command reads 10000 bytes starting at byte address 100. If the image has 100 pixels per row, the first byte the routine reads will be the first pixel in the third row of the image.</p>

<pre><small>lwdaq_data_manipulate image_name read 100 10000</small></pre>

<p>The <i>shift</i> function requires one parameter: the number of bytes to the left by which you want the data to be shifted. Shifting to the left is in the direction of the start of the data space. If you specify a negative shift, the routine shifts the data to the right, in the direction of the end of the data space.</p>

<p>The <i>clear</i> function takes no parameters. It clears all the byte in the data space to zero.</p>
}
function lwdaq_data_manipulate(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	option,value:short_string;
	arg_index:integer;
	image_name:short_string='';
	manipulation:short_string='none';
	ip,nip:image_ptr_type;
	data_obj,data_ptr:pointer=nil;
	vp:pointer;	
	data_size,byte_address:integer=-1;
	shift:integer=0;
	
begin
	error_string:='';
	lwdaq_data_manipulate:=Tcl_Error;
{
	This routine needs at least three arguments: the routine name, the image name, and
	the manipulation name.
}
	if (argc<3) then begin
		Tcl_SetReturnShortString(interp,'Wrong number of arguments, must be '
			+'"lwdaq_data_manipulate image_name manipulation ?parameters?".');
		exit;
	end;
{
	Get the image name and manipulation name.
}
	arg_index:=1;
	image_name:=Tcl_ObjShortString(argv[arg_index]);
	inc(arg_index);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	manipulation:=Tcl_ObjShortString(argv[arg_index]);
	inc(arg_index);
{
	Perform the specified manipulation.
}
	if manipulation='write' then begin
		if argc<arg_index+2 then begin
			Tcl_SetReturnShortString(interp,'Specify byte_address and data.');
			exit;
		end;
		byte_address:=Tcl_ObjInteger(argv[arg_index]);
		inc(arg_index);
		if byte_address<0 then begin
			Tcl_SetReturnShortString(interp,'Start address less than zero.');
			exit;
		end;
		data_obj:=argv[arg_index];
		inc(arg_index);
		data_ptr:=Tcl_GetByteArrayFromObj(data_obj,data_size);
		if data_size<0 then begin
			Tcl_SetReturnShortString(interp,'Data size less than zero.');
			exit;
		end;
		if byte_address+data_size>sizeof(ip^.intensity)-ip^.i_size then begin
			Tcl_SetReturnShortString(interp,'Data extends past end of image.');
			exit;
		end;
		block_move(data_ptr,
			pointer(integer(@ip^.intensity[1,0])+byte_address),
			data_size);
		Tcl_SetReturnShortString(interp,'');
	end else if manipulation='read' then begin
		if argc<arg_index+2 then begin
			Tcl_SetReturnShortString(interp,'Specify data size and byte address.');
			exit;
		end;
		byte_address:=Tcl_ObjInteger(argv[arg_index]);
		inc(arg_index);
		if byte_address<0 then begin
			Tcl_SetReturnShortString(interp,'Start address less than zero.');
			exit;
		end;
		data_size:=Tcl_ObjInteger(argv[arg_index]);
		inc(arg_index);
		if data_size<0 then begin
			Tcl_SetReturnShortString(interp,'Requested data size less than zero.');
			exit;
		end;
		if byte_address+data_size>sizeof(ip^.intensity)-ip^.i_size then begin
			Tcl_SetReturnShortString(interp,'Requested data extends past end of image.');
			exit;
		end;
		Tcl_SetReturnByteArray(interp,
			pointer(integer(@ip^.intensity[1,0])+byte_address),
			data_size);
	end else if manipulation='shift' then begin
		if argc<arg_index+1 then begin
			Tcl_SetReturnShortString(interp,'Specify shift in bytes, positive left.');
			exit;
		end;
		shift:=Tcl_ObjInteger(argv[arg_index]);
		nip:=new_image(ip^.j_size,ip^.i_size);
		if nip=nil then begin
			Tcl_SetReturnShortString(interp,'Failed to allocate memory for new image.');
			exit;
		end;
		if shift>0 then begin
			block_move(pointer(integer(@ip^.intensity[1,0])+shift),
				@nip^.intensity[1,0],
				sizeof(ip^.intensity)-ip^.j_size-shift);
		end else begin
			block_move(@ip^.intensity[1,0],
				pointer(integer(@nip^.intensity[1,0])+shift),
				sizeof(ip^.intensity)-ip^.j_size-shift);
		end;
		block_move(@nip^.intensity[1,0],@ip^.intensity[1,0],sizeof(ip^.intensity)-ip^.j_size);
		dispose_image(nip);
		Tcl_SetReturnShortString(interp,'');
	end else if manipulation='clear' then begin
		with ip^ do block_clear(@intensity[1,0],sizeof(ip^.intensity)-ip^.j_size);
		Tcl_SetReturnShortString(interp,'');
	end else if manipulation='none' then begin 
		{no action}
	end else begin
		Tcl_SetReturnShortString(interp,'Bad manipulation "'+manipulation
			+'", must be one of "read write shift clear none".');
		exit;
	end;
{
	Return error string.
}
	if error_string<>'' then Tcl_SetReturnShortString(interp,error_string);
	lwdaq_data_manipulate:=Tcl_OK;
end;


{
<p>lwdaq_rasnik analyzes rasnik images. Specify the image with -image_name as usual. The routine clears the image overlay for its own use. The routine takes the following options, each of which you specify by giving the option name followed by its value, ?option value?. See the <a href="">Rasnik Instrument</a> for a description of the options.</p>

<table border cellspacing=2>
<tr><th>Option</th><th>Function</th></tr>
<tr><td>-reference_code</td><td>Selects the analysis reference point.</td></tr>
<tr><td>-reference_x_um</td><td>Sets x-coordinate of reference point when -reference_code=3.</td></tr>
<tr><td>-reference_y_um</td><td>Sets y-coordinate of reference point when -reference_code=3.</td></tr>
<tr><td>-orientation_code</td><td>Selects the analysis orientation code.</td></tr>
<tr><td>-square_size_um</td><td>Tells the analysis the mask square size (assumed square).</td></tr>
<tr><td>-pixel_size_um</td><td>Tells the analysis the pixel size (assumed square)</td></tr>
<tr><td>-show_timinig</td><td>If 1, print timing report to gui text window.</td></tr>
<tr><td>-show_fitting</td><td>If <> 0, show fitting stages with delay $value ms.</td></tr>
</table>

<p>See the <a href="http://alignment.hep.brandeis.edu/Electronics/LWDAQ/Manual.html#Rasnik">Rasnik Instrument</a> Manual for more information about the option values, in particular the reference and orientation code meanings.</p>
}
function lwdaq_rasnik(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

const
	rnw=8;rnd=3;
		
var 
	ip,iip,jip:image_ptr_type=nil;
	image_name,result:short_string='';
	pp:rasnik_pattern_ptr_type=nil;
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	show_fitting,show_timing:boolean=false;
	square_size_um:real=120;
	pixel_size_um:real=10;
	reference_code,orientation_code:integer=0;
	rp:rasnik_ptr_type;
	reference_x_um,reference_y_um:real=0;
		
begin
	error_string:='';
	lwdaq_rasnik:=Tcl_Error;		

	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_rasnik image ?option value?".');
		exit;
	end;

	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-show_fitting') then show_fitting:=Tcl_ObjBoolean(vp)
		else if (option='-show_timing') then show_timing:=Tcl_ObjBoolean(vp)
		else if (option='-reference_code') then reference_code:=Tcl_ObjInteger(vp)
		else if (option='-orientation_code') then orientation_code:=Tcl_ObjInteger(vp)
		else if (option='-square_size_um') then square_size_um:=Tcl_ObjReal(vp)
		else if (option='-pixel_size_um') then pixel_size_um:=Tcl_ObjReal(vp)
		else if (option='-reference_x_um') then reference_x_um:=Tcl_ObjReal(vp)
		else if (option='-reference_y_um') then reference_y_um:=Tcl_ObjReal(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-reference_code -orientation_code'
				+' -square_size_um -pixel_size_um -reference_x_um -reference_y_um'
				+' -show_fitting".');
			exit;
		end;
	end;

	start_timer('generating image derivatives',CurrentRoutineName);
	iip:=image_grad_i(ip);
	jip:=image_grad_j(ip);
	mark_time('clearing overlay',CurrentRoutineName);
	clear_overlay(ip);
	mark_time('starting rasnik_find_pattern',CurrentRoutineName);
	pp:=rasnik_find_pattern(iip,jip,show_fitting);
	if show_fitting then begin
		rasnik_display_pattern(ip,pp,false);
		gui_draw(ip^.name);
		gui_wait('Approximate pattern from slices.');
	end;
	mark_time('starting rasnik_refine_pattern',CurrentRoutineName);
	rasnik_refine_pattern(pp,iip,jip,show_fitting);
	mark_time('starting rasnik_adjust_pattern_parity',CurrentRoutineName);
	rasnik_adjust_pattern_parity(ip,pp);
	mark_time('starting rasnik_identify_pattern_squares',CurrentRoutineName);
	rasnik_identify_pattern_squares(ip,pp);
	mark_time('starting rasnik_identify_code_squares',CurrentRoutineName);
	rasnik_identify_code_squares(ip,pp);
	mark_time('starting rasnik_analyze_code',CurrentRoutineName);
	rasnik_analyze_code(pp,orientation_code);
	mark_time('starting rasnik_from_pattern',CurrentRoutineName);
	rp:=rasnik_from_pattern(ip,pp,reference_code,reference_x_um,reference_y_um,
		square_size_um,pixel_size_um);
	mark_time('starting rasnik_display_pattern',CurrentRoutineName);
	rasnik_display_pattern(ip,pp,show_fitting);
	rasnik_display_reference_point(ip,reference_code,reference_x_um,reference_y_um,
		pixel_size_um);
	writestr(result,string_from_rasnik(rp^));
	mark_time('starting to dispose pointers',CurrentRoutineName);
	dispose_rasnik_pattern(pp);
	dispose_rasnik(rp);
	dispose_image(iip);
	dispose_image(jip);
	mark_time('done',CurrentRoutineName);
	if show_timing then report_time_marks;
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_rasnik:=Tcl_OK;
end;

{
<p>lwdaq_rasnik_shift takes in a rasnik result string and shifts it to a new reference point. The routine gets the old reference point from the results string, and re-calculates the rasnik measurement using the x and y coordinates you specify with -reference_x_um and -reference_y_um.</p>
}
function lwdaq_rasnik_shift(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

const
	rnw=8;rnd=3;
		
var 
	ref:xy_point_type;
	old_result,result:short_string='';
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	rasnik:rasnik_type;
	reference_x_um,reference_y_um:real=0;
	source_name:short_string='';
		
begin
	error_string:='';
	lwdaq_rasnik_shift:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_rasnik_shift old_result ?option value?".');
		exit;
	end;
	
	old_result:=Tcl_ObjShortString(argv[1]);
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-reference_x_um') then reference_x_um:=Tcl_ObjReal(vp)
		else if (option='-reference_y_um') then reference_y_um:=Tcl_ObjReal(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-reference_x_um -reference_y_um".');
			exit;
		end;
	end;
	
	source_name:=read_word(old_result);
	rasnik:=rasnik_from_string(old_result);
	ref.x:=reference_x_um;
	ref.y:=reference_y_um;
	rasnik:=rasnik_shift_reference_point(rasnik,ref);
	result:=source_name+' '+string_from_rasnik(rasnik);
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_rasnik_shift:=Tcl_OK;
end;

{
<p>lwdaq_bcam analyzes bcam images. The routine clears the image overlay for its own use.</p>

<table border cellspacing=2>
<tr><th>Option</th><th>Function</th></tr>
<tr><td>-num_spots</td><td>The number of spots the analysis should find and answer for.</td></tr>
<tr><td>-threshold</td><td>The intensity threshold for eliminating background level.</td></tr>
<tr><td>-color</td><td>Color for spot marking in overlay, default red.</td></tr>
<tr><td>-pixel_size_um</td><td>Tells the analysis the pixel size (assumed square)</td></tr>
<tr><td>-show_timinig</td><td>If 1, print timing report to gui text window.</td></tr>
</table>

<p>The analysis subtracts the -threshold from all pixels within the image's analysis bounds. Any pixels with negative intensity are set to zero. The routine identifies all distinct sets of contiguous pixels above threshold, determines the centroid of each of them, and returns the characteristics of the -num_spots brightes sets. The centroid coordinates are in microns with respect to the top-left corner of the image. To convert from pixels to microns, the routine uses -pixel_size_um, and it assumes the pixels are square. See the <a href="http://alignment.hep.brandeis.edu/Electronics/LWDAQ/Manual.html#BCAM">BCAM Instrument</a> Manual for more information about the option values.</p>
}
function lwdaq_bcam(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

const
	rnw=8;rnd=3;
		
var 
	ip,nip:image_ptr_type=nil;
	image_name,result:short_string='';
	option,value:short_string;
	arg_index,spot_num:integer;
	vp:pointer;	
	show_timing:boolean=false;
	pixel_size_um:real=10;
	threshold:integer=50;
	color:integer=red_color;
	num_spots:integer=1;
	slp:spot_list_ptr_type;
	spot:spot_type;
		
begin
	error_string:='';
	lwdaq_bcam:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_bcam image ?option value?".');
		exit;
	end;
	
	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-show_timing') then show_timing:=Tcl_ObjBoolean(vp)
		else if (option='-pixel_size_um') then pixel_size_um:=Tcl_ObjReal(vp)
		else if (option='-threshold') then threshold:=Tcl_ObjInteger(vp)			
		else if (option='-num_spots') then num_spots:=Tcl_ObjInteger(vp)			
		else if (option='-color') then color:=Tcl_ObjInteger(vp)			
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'-threshold -pixel_size_um -show_timing -num_spots -color".');
			exit;
		end;
	end;
	
	if image_name='' then begin
		Tcl_SetReturnShortString(interp,'Specify an image name with -image_name.');
		exit;
	end;
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	start_timer('clearing overlay',CurrentRoutineName);
	clear_overlay(ip);
	mark_time('starting spot_list_find',CurrentRoutineName);
	slp:=spot_list_find(ip,num_spots,threshold,pixel_size_um);
	mark_time('calculating centroids',CurrentRoutineName);
	for spot_num:=1 to num_spots do 
		spot_centroid(ip,slp^.spots[spot_num]);
	mark_time('displaying spot_list',CurrentRoutineName);
	clear_overlay(ip);
	if num_spots>1 then spot_list_display_bounds(ip,slp,color);
	if num_spots=1 then spot_list_display_crosses(ip,slp,color);
	mark_time('done',CurrentRoutineName);
	
	result:=string_from_spot_list(slp);
	dispose_spot_list_ptr(slp);
	if num_spots=0 then result:="";
	if show_timing then report_time_marks;
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_bcam:=Tcl_OK;
end;

{
<p>lwdaq_wps analyzes wps images. It clears the overlay for its own use. We describe the analysis in our <a href="http://www.opensourceinstruments.com/WPS/WPS1/">WPS1 Manual</a>.</p>

<table border cellspacing=2>
<tr><th>Option</th><th>Function</th></tr>
<tr><td>-pixel_size_um</td><td>Width and height of image pixels in microns.</td></tr>
<tr><td>-reference_um</td><td>Location of reference line in microns below top edge of top row.</td></tr>
<tr><td>-show_timinig</td><td>If 1, print timing report to gui text window, default zero.</td></tr>
<tr><td>-show_edges</td><td>If 1, show edge pixesls in image, defalut zero</td></tr>
<tr><td>-num_wires</td><td>The number of wires you want the routine to find.</td></tr>
<tr><td>-threshold</td><td>Intensity threshold in the gradient image for detecting edge pixels.</td></tr>
</table>

<p>The wire positions are given with respect to a horizontal reference line drawing <i>reference_um</i> microns down from the top edge of the top image row. The threshold must apply to the gradient image, which lwdaq_wps will obtain with image_grad_i. If the calling routine is going to determine the threshold based upon the minimum, average, and maximum intensities in the gradient image, the calling routine must also obtain the gradient image. In the TCL interpreter, you obtain the gradient image using [lwdaq_image_manipulate $image_name grad_i].</p>

<p>With <i>show_edges</i> equal to zero (the default value), the routine plots the image's horizontal intensity profile in green and the derivative profile in yellow. But when you set <i>show_edges</i> to 1, the routine no longer plots these two graphs, but instead displays the spots it finds in the derivative image, but overlayed upon the original image. The edges of a wire will be covered with colored pixels.</p>

}
function lwdaq_wps(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

const
	rnw=8;rnd=3;
	spots_per_wire=2;
		
var 
	ip,iip:image_ptr_type=nil;
	image_name,result:short_string='';
	option,value:short_string;
	arg_index:integer;
	reference_um:real=0;
	spot_num,num_spots,threshold:integer;
	vp:pointer;	
	show_timing,show_edges:boolean=false;
	pixel_size_um:real=10;
	num_wires,i,j:integer=1;
	slp:spot_list_ptr_type;
	pp:x_graph_ptr_type;
	saved_bounds:ij_rectangle_type;
	ref_line:ij_line_type;
		
begin
	error_string:='';
	lwdaq_wps:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_wps image ?option value?".');
		exit;
	end;
	
	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-show_timing') then show_timing:=Tcl_ObjBoolean(vp)
		else if (option='-show_edges') then show_edges:=Tcl_ObjBoolean(vp)
		else if (option='-pixel_size_um') then pixel_size_um:=Tcl_ObjReal(vp)
		else if (option='-reference_um') then reference_um:=Tcl_ObjReal(vp)
		else if (option='-num_wires') then num_wires:=Tcl_ObjInteger(vp)
		else if (option='-threshold') then threshold:=Tcl_ObjInteger(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'-pixel_size_um -show_timing -num_wires '
				+'-threshold -show_edges".');
			exit;
		end;
	end;
	
	if image_name='' then begin
		Tcl_SetReturnShortString(interp,'Specify an image name with -image_name.');
		exit;
	end;
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
{
	Generate the derivative image and find spots above threshold in this new
	image.
}
	start_timer('generating derivative image',CurrentRoutineName);
	iip:=image_grad_i(ip);
	start_timer('starting spot_list_find',CurrentRoutineName);
	num_spots:=spots_per_wire*num_wires;
	slp:=spot_list_find(iip,num_spots,threshold,pixel_size_um);
	spot_list_sort(slp,spot_increasing_x);
{
	Fit lines to the spots.
}
	mark_time('calculating vertical lines',CurrentRoutineName);
	for spot_num:=1 to num_spots do
		spot_vertical_line(iip,slp^.spots[spot_num]);
{
	Display graphical results of analysis.
}
	if show_edges then begin
		mark_time('displaying edges',CurrentRoutineName);
		for j:=0 to ip^.j_size-1 do
			for i:=0 to ip^.i_size-1 do 
				ip^.overlay[j,i]:=iip^.overlay[j,i];
	end else begin
		mark_time('displaying derivative profile',CurrentRoutineName);
		saved_bounds:=ip^.analysis_bounds;
		pp:=image_profile_row(iip);
		ip^.analysis_bounds:=iip^.analysis_bounds;
		display_profile_row(ip,pp,yellow_color);
		ip^.analysis_bounds:=saved_bounds;
		dispose_x_graph(pp);
		mark_time('displaying intensity profile',CurrentRoutineName);
		pp:=image_profile_row(ip);
		display_profile_row(ip,pp,green_color);
		ip^.analysis_bounds:=saved_bounds;
		dispose_x_graph(pp);
	end;
	mark_time('displaying lines',CurrentRoutineName);
	spot_list_display_vertical_lines(ip,slp,red_color);
	ref_line.a.i:=ip^.analysis_bounds.left;
	ref_line.a.j:=round(reference_um/pixel_size_um);
	ref_line.b.i:=ip^.analysis_bounds.right;
	ref_line.b.j:=round(reference_um/pixel_size_um);
	display_ccd_line(ip,ref_line,blue_color);	
{
	Shift x-position of lines so that each line position is given
	as the intersection of the line and a horizontal line reference_um
	microns down from the top row in the image.
}
	for spot_num:=1 to num_spots do 
		with slp^.spots[spot_num] do
			if valid then
				x:=x+reference_um*y/mrad_per_rad;
{
	Dispose of the spot list and return the numerical results.
}
	mark_time('done',CurrentRoutineName);
	result:=string_from_spot_list(slp);
	dispose_spot_list_ptr(slp);
	dispose_image(iip);
	if num_spots=0 then result:="";
	if show_timing then report_time_marks;
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_wps:=Tcl_OK;
end;

{
<p>lwdaq_calibration takes as input an apparatus measurement and a device calibration,
and returns a parameter calculation.</p>
}
function lwdaq_calibration(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

const
	rnw=8;rnd=3;
		
var 
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	dev_calib,app_meas,rsult:short_string;
	verbose,check_spread:boolean=false;
		
begin
	error_string:='';
	lwdaq_calibration:=Tcl_Error;
	
	if (argc<3) or (not odd(argc)) then begin
		Tcl_SetReturnShortString(interp,'Wrong number of arguments, must be ' 
			+'"lwdaq_image_calibration device_calibration apparatus_measurement'
			+' ?option value?".');
		exit;
	end;
	
	arg_index:=3;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-verbose') then verbose:=Tcl_ObjBoolean(vp)
		else if (option='-check_spread') then check_spread:=Tcl_ObjBoolean(vp)
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-verbose -check_spread".');

			exit;
		end;
	end;
	
	dev_calib:=Tcl_ObjShortString(argv[1]);	
	app_meas:=Tcl_ObjShortString(argv[2]);	
	
	if error_string='' then Tcl_SetReturnShortString(interp,
		parameter_calculation(dev_calib,app_meas,verbose,check_spread))
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_calibration:=Tcl_OK;
end;

{
<p>lwdaq_diagnostic analyzes sixteen-bit adc samples from the driver supplies. It assumes that five numbers specifying the relay software version, the driver assembly number, the driver hardware version, the controller firmware version, and the data transfer speed are all saved in the input image's results string. The routine leaves these numbers in the results string after it is done.</p>
}
function lwdaq_diagnostic(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	image_name,result:short_string='';
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	v_min,v_max,t_min,t_max:real=0;
	ac_couple:boolean=false;
	 
begin
	error_string:='';
	lwdaq_diagnostic:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_diagnostic image ?option value?".');
		exit;
	 end;

	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-v_min') then v_min:=Tcl_ObjReal(vp)			
		else if (option='-v_max') then v_max:=Tcl_ObjReal(vp)			
		else if (option='-t_max') then t_max:=Tcl_ObjReal(vp)			
		else if (option='-t_min') then t_min:=Tcl_ObjReal(vp)			
		else if (option='-ac_couple') then ac_couple:=Tcl_ObjBoolean(vp)			
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-v_max -v_min -t_max -t_min -ac_couple".');
			exit;
		end;
	end;
	
	result:=lwdaq_A2037_monitor(ip,t_min,t_max,v_min,v_max,ac_couple);
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_diagnostic:=Tcl_OK;
end;

{
<p>lwdaq_voltmeter analyzes image data for the Voltmeter instrument.</p>
}
function lwdaq_voltmeter(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	image_name,result:short_string='';
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	v_min,v_max,v_trigger,t_min,t_max:real=0;
	ac_couple,auto_calib,positive_trigger:boolean=false;
	 
begin
	error_string:='';
	lwdaq_voltmeter:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_voltmeter image ?option value?".');
		exit;
	end;
	
	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-v_min') then v_min:=Tcl_ObjReal(vp)			
		else if (option='-v_max') then v_max:=Tcl_ObjReal(vp)			
		else if (option='-v_trigger') then v_trigger:=Tcl_ObjReal(vp)			
		else if (option='-t_max') then t_max:=Tcl_ObjReal(vp)			
		else if (option='-t_min') then t_min:=Tcl_ObjReal(vp)			
		else if (option='-ac_couple') then ac_couple:=Tcl_ObjBoolean(vp)			
		else if (option='-positive_trigger') then positive_trigger:=Tcl_ObjBoolean(vp)			
		else if (option='-auto_calib') then auto_calib:=Tcl_ObjBoolean(vp)			
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'-v_max -v_min -t_max -t_min -ac_couple -auto_calib".');
			exit;
		end;
	end;
	result:=lwdaq_A2057_voltmeter(ip,t_min,t_max,v_min,v_max,v_trigger,
		ac_couple,positive_trigger,auto_calib);
		
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_voltmeter:=Tcl_OK;
end;

{
<p>lwdaq_rfpm analyzes images from an RFPM instrument.</p>
}
function lwdaq_rfpm(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	image_name,result:short_string='';
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	v_min,v_max,v_trigger,t_min,t_max:real=0;
	rms:boolean=false;
	 
begin
	error_string:='';
	lwdaq_rfpm:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_rfpm image ?option value?".');
		exit;
	end;
	
	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-v_min') then v_min:=Tcl_ObjReal(vp)			
		else if (option='-v_max') then v_max:=Tcl_ObjReal(vp)			
		else if (option='-rms') then rms:=Tcl_ObjBoolean(vp)			
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-v_max -v_min -rms".');
			exit;
		end;
	end;
	result:=lwdaq_A3008_rfpm(ip,v_min,v_max,rms);
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_rfpm:=Tcl_OK;
end;

{
<p>lwdaq_inclinometer analyzes an image returned by the Inclinometer instrument. It returns the amplitude of harmonics in signals recorde in an image.</p>
}
function lwdaq_inclinometer(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	image_name,result:short_string='';
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	v_min,v_max,v_trigger:real=0;
	harmonic:real=1;

begin
	error_string:='';
	lwdaq_inclinometer:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_inclinometer image ?option value?".');
		exit;
	end;

	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-v_min') then v_min:=Tcl_ObjReal(vp)			
		else if (option='-v_max') then v_max:=Tcl_ObjReal(vp)	
		else if (option='-harmonic') then harmonic:=Tcl_ObjReal(vp)	
		else if (option='-v_trigger') then v_trigger:=Tcl_ObjReal(vp)	
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-v_trigger -v_max -v_min -harmonic".');
			exit;
		end;
	end;
	result:=lwdaq_A2065_inclinometer(ip,v_trigger,v_min,v_max,harmonic);
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_inclinometer:=Tcl_OK;
end;

{
<p>lwdaq_recorder steps through the pixels of an image looking for valid messages from an asynchronous transmitter such as the Subcutaneous Transmitter (<a href="http://www.opensourceinstruments.com/Electronics/A3013/M3013.html">A3013</a>) as received by a Data Receiver (<a href="http://www.opensourceinstruments.com/Electronics/A3018/M3018.html">A3018</a>). It draws the signals it discovers in the image overlay. See the <a href="http://www.opensourceinstruments.com/Electronics/A3018/Recorder.html">Recorder Instrument</a> Manual for details.</p>
}
function lwdaq_recorder(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	image_name:short_string='';
	lsp:long_string_ptr;
	command,instruction:short_string='';
	error:integer;

begin
	error_string:='';
	lwdaq_recorder:=Tcl_Error;
	
	if argc<>3 then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_recorder image command".');
		exit;
	end;

	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	command:=Tcl_ObjShortString(argv[2]);
	
	lsp:=lwdaq_A3007_recorder(ip,command);
	lwdaq_long_string:=lsp^;
	dispose_long_string(lsp);
	
	if error_string='' then Tcl_SetReturnLongString(interp,lwdaq_long_string)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_recorder:=Tcl_OK;
end;

{
<p>lwdaq_sampler steps through the pixels of an image looking for valid samples from a sampling circuit like the ADC Tester (<a href="http://alignment.hep.brandeis.edu/Electronics/A2100/M2100.html">A2100</a>).</p>
}
function lwdaq_sampler(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	image_name:short_string='';
	lsp:long_string_ptr;
	command,instruction:short_string='';
	error:integer;

begin
	error_string:='';
	lwdaq_sampler:=Tcl_Error;
	
	if argc<>3 then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_sampler image command".');
		exit;
	end;

	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	command:=Tcl_ObjShortString(argv[2]);
	
	lsp:=lwdaq_A2100_sampler(ip,command);
	lwdaq_long_string:=lsp^;
	dispose_long_string(lsp);
	
	if error_string='' then Tcl_SetReturnLongString(interp,lwdaq_long_string)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_sampler:=Tcl_OK;
end;

{
<p>lwdaq_gauge analyzes sixteen-bit adc values by calling lwdaq_A2053_gauge. The routine assumes that two numbers specifying the sample period and the number of channels sampled are saved in the input image's results string. The routine leaves these numbers in the results string after it is done. For each gauge channel in the image, the routine returns a result, according to the result specifiers. With -ave 1, the result for each channel includes the average gauge value. With -stdev 1, the result includes the standard deviation of the gauge value. With both set to zero, the result is an empty string. The default values for ave and stdev are 1 and 0 respectively.</p>
}
function lwdaq_gauge(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	image_name,result:short_string='';
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	y_min,y_max,t_min,t_max:real=0;
	ref_bottom:real=0;
	ref_top:real=100;
	ac_couple,stdev:boolean=false;
	ave:boolean=true;
	
begin
	error_string:='';
	lwdaq_gauge:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_gauge image ?option value?".');
		exit;
	end;

	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-y_min') then y_min:=Tcl_ObjReal(vp)			
		else if (option='-y_max') then y_max:=Tcl_ObjReal(vp)			
		else if (option='-t_max') then t_max:=Tcl_ObjReal(vp)			
		else if (option='-t_min') then t_min:=Tcl_ObjReal(vp)			
		else if (option='-ref_bottom') then ref_bottom:=Tcl_ObjReal(vp)			
		else if (option='-ref_top') then ref_top:=Tcl_ObjReal(vp)			
		else if (option='-ac_couple') then ac_couple:=Tcl_ObjBoolean(vp)			
		else if (option='-stdev') then stdev:=Tcl_ObjBoolean(vp)			
		else if (option='-ave') then ave:=Tcl_ObjBoolean(vp)			
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
						+'"image -y_max -y_min -t_max -t_min -ac_couple -stdev -ave".');
			exit;
		end;
	end;
	result:=lwdaq_A2053_gauge(ip,t_min,t_max,y_min,y_max,
		ac_couple,ref_bottom,ref_top,
		ave,stdev);
		
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_gauge:=Tcl_OK;
end;

{
<p>lwdaq_flowmeter analyzes sixteen-bit adc values by calling lwdaq_A2053_flowmeter. It assumes that two numbers specifying the sample period and the number of channels sampled are saved in the input image's results string. The routine leaves these numbers in the results string after it is done.</p>
}
function lwdaq_flowmeter(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	image_name,result:short_string='';
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	c_min,c_max,t_min,t_max:real=0;
	ref_bottom:real=15.38;
	ref_top:real=25.69;
	 
begin
	error_string:='';
	lwdaq_flowmeter:=Tcl_Error;
	
	if (argc<2) or (odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_flowmeter image ?option value?".');
		exit;
	end;

	image_name:=Tcl_ObjShortString(argv[1]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	arg_index:=2;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-c_min') then c_min:=Tcl_ObjReal(vp)			
		else if (option='-c_max') then c_max:=Tcl_ObjReal(vp)			
		else if (option='-t_max') then t_max:=Tcl_ObjReal(vp)			
		else if (option='-t_min') then t_min:=Tcl_ObjReal(vp)			
		else if (option='-ref_bottom') then ref_bottom:=Tcl_ObjReal(vp)			
		else if (option='-ref_top') then ref_top:=Tcl_ObjReal(vp)			
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
						+'"-c_max -c_min -t_max -t_min".');
			exit;
		end;
	end;
	result:=lwdaq_A2053_flowmeter(ip,t_min,t_max,c_min,c_max,ref_bottom,ref_top);
	
	if error_string='' then Tcl_SetReturnShortString(interp,result)
	else Tcl_SetReturnShortString(interp,error_string);
	lwdaq_flowmeter:=Tcl_OK;
end;

{
<p>lwdaq_graph takes a string from TCL that contains consecutive x-y value pairs, and plots a graph of the values in the overlay of an image. The routine fills the analysis bounds of the image with a graph, or it fills the entire image with the graph. You can specify the values of x and y that correspond to the edges of the plotting area. If you do not specify the edgs, the routine will stretch or compress the plot to fit exactly in the available space.</p>
<table border cellspacing=2>
<tr><th>Option</th><th>Value and Effect</th></tr>
<tr><td>-x_min</td><td>x at left edge, if 0 with x_max, pick minimum value of x.</td></tr>
<tr><td>-x_max</td><td>x at right edge, if 0 with x_min, pick maximum value of x.</td></tr>
<tr><td>-y_min</td><td>y at bottom edge, if 0 with y_max, pick minimum value of y.</td></tr>
<tr><td>-y_max</td><td>y at top edge, if 0 with y_min, pick maximum value of y.</td></tr>
<tr><td>-color</td><td>integer code for the color.</td></tr>
<tr><td>-clear</td><td>if 1, clear image overlay before plotting.</td></tr>
<tr><td>-fill</td><td>if 1, fill image overlay before plotting.</td></tr>
<tr><td>-x_div</td><td>if > 0, plot vertical divisions spaced by this amount.</td></tr>
<tr><td>-y_div</td><td>if > 0, plot horizontal divisions spaced by this amount.</td></tr>
<tr><td>-entire</td><td>if 1, use entire image for plot, if 0, use analysis bounds.</td></tr>
</table>

<p>The color codes for the graph give 255 unique colors. You can try them out to see which ones you like.</p>

}
function lwdaq_graph(data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var 
	ip:image_ptr_type=nil;
	gp,ggp:xy_graph_ptr_type=nil;
	image_name:short_string='';
	option,value:short_string;
	arg_index:integer;
	vp:pointer;	
	x_min,x_max,y_min,y_max:real=0;
	x_div,y_div:real=0;
	num_points,point_num:integer=0;
	lsp:long_string_ptr;
	color:integer=black_color;
	clear,entire,fill:boolean=false;
	x,y:real;
	saved_bounds:ij_rectangle_type;
	
begin
	error_string:='';
	lwdaq_graph:=Tcl_Error;
	
	if (argc<3) or (not odd(argc)) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be "lwdaq_graph data image ?option value?".');
		exit;
	end;

	arg_index:=3;
	while (arg_index<argc-1) do begin
		option:=Tcl_ObjShortString(argv[arg_index]);
		inc(arg_index);
		vp:=argv[arg_index];
		inc(arg_index);
		if (option='-x_min') then x_min:=Tcl_ObjReal(vp)			
		else if (option='-x_max') then x_max:=Tcl_ObjReal(vp)			
		else if (option='-y_max') then y_max:=Tcl_ObjReal(vp)			
		else if (option='-y_min') then y_min:=Tcl_ObjReal(vp)			
		else if (option='-color') then color:=Tcl_ObjInteger(vp)			
		else if (option='-clear') then clear:=Tcl_ObjBoolean(vp)			
		else if (option='-entire') then entire:=Tcl_ObjBoolean(vp)			
		else if (option='-fill') then fill:=Tcl_ObjBoolean(vp)			
		else if (option='-x_div') then x_div:=Tcl_ObjReal(vp)			
		else if (option='-y_div') then y_div:=Tcl_ObjReal(vp)			
		else begin
			Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
				+'"-x_max -x_min -y_max -y_min -clear -color -x_div -y_div '
				+'"-fill -entire".');

			exit;
		end;
	end;
	
	image_name:=Tcl_ObjShortString(argv[2]);
	ip:=image_ptr_from_name(image_name);
	if not valid_image_ptr(ip) then begin
		Tcl_SetReturnShortString(interp,'Image "'+image_name+'" does not exist.');
		exit;
	end;
	if clear then clear_overlay(ip);
	if fill then fill_overlay(ip);
	if entire then begin
		saved_bounds:=ip^.analysis_bounds;
		with ip^.analysis_bounds do begin
			left:=0;
			top:=0;
			right:=ip^.i_size-1;
			bottom:=ip^.j_size-1;
		end;
	end;

	lsp:=Tcl_ObjLongString(argv[1]);
	gp:=read_xy_graph(lsp^);
	display_real_graph(ip,gp,overlay_color_from_integer(color),
		x_min,x_max,y_min,y_max,x_div,y_div);		
	dispose_xy_graph(gp);
	dispose_long_string(lsp);
	if entire then ip^.analysis_bounds:=saved_bounds;
	
	if error_string<>'' then Tcl_SetReturnShortString(interp,error_string);
	lwdaq_graph:=Tcl_OK;
end;

{
<p>The <i>lwdaq</i> command acts as an entry point into our analysis libraries, making various math functions available at the TCL command line. You specify the routine you wish to call, and pass arguments to the routine in strings or byte arrays or both. Most routines return results as text strings in which real numbers are encoded in characters with a fixed number of decimal places, as defined by the global constants <i>fsr</i> and <i>fsd</i>. You can set both of these with <i>lwdaq_config</i>. Beware that these routines can round small values to zero. In the comments below, we assume that <i>fsr</i> is 8, and <i>fsd</i> is 6.</p>
}
function lwdaq (data,interp:pointer;argc:integer;var argv:Tcl_ArgList):integer;

var
	option,result,s:short_string='';
	slope,intercept,rms_residual,position,interpolation:real;
	period,amplitude,offset,average:real;
	a,b:sinusoid_type;
	gp:xy_graph_ptr_type;
	gpx,periods:x_graph_ptr_type;
	lsp:long_string_ptr;
	M,N:matrix_ptr;
	i:integer;
	
begin
	error_string:='';
	lwdaq:=Tcl_Error;
		
	if (argc<2) then begin
		Tcl_SetReturnShortString(interp,
			'Wrong number of arguments, must be: "lwdaq option ?args?".');
		exit;
	end;

	option:=Tcl_ObjShortString(argv[1]);
	if option='bcam_from_global_point' then begin
{
<p>Transforms a point in global coordinates to a point in BCAM coordinates. The point in BCAM coordinates is returned as a string of three numbers, the BCAM <i>x</i>, <i>y</i>, and <i>z</i> coordinates of the point. You specify the point in global coordinates with the <i>point</i> parameter, which also takes the form of a string of three numbers, these numbers being the global <i>x</i>, <i>y</i>, and <i>z</i> coordinates of the point whose BCAM coordinates you want to determine. You specify how the BCAM and global coordinate systems relate to one another with the <i>mount</i> string. The <i>mount</i> string contains the global coordinates of the BCAM's kinematic mounting balls. You specify the coordinates of the cone, slot, and flat balls, and for each ball you give its <i>x</i>, <i>y</i>, and <i>z</i> coordinates. In the following example, we transform the global point (0,1,0) into BCAM coordinates when our cone, slot and flat balls have coordinates (0,1,0), (-1,1,-1), and (1,1,-1).</p>

<pre>
% lwdaq bcam_from_global_point "0 1 0" "0 1 0 -1 1 -1 1 1 -1"
0.000000 0.000000 0.000000
</pre>

<p>For a description of the BCAM coordinate system, and how it is defined with respect to a BCAM's kinematic mounting balls, consult the BCAM <a href="http://alignment.hep.brandeis.edu/Devices/BCAM/User_Manual.html">User Manual</a>. We usually use millimeters to specify coordinates, because we use millimeters in our BCAM camera and source calibration constants. But the routine will work with any units of length, so long as you use the same units for both the point and the mount strings.</p>
}
		if (argc<>4) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq bcam_from_global_point point mount".');
			exit;
		end;
		Tcl_SetReturnShortString(interp,
			string_from_xyz(
				bcam_from_global_point(
					xyz_from_string(Tcl_ObjShortString(argv[2])),
					kinematic_mount_from_string(Tcl_ObjShortString(argv[3])))));
	end 
	else if option='global_from_bcam_point' then begin
{
<p>Transforms a point in global coordinates to a point in BCAM coordinates. It is the inverse of <a href="bcam_from_global_point"</a>. You pass it the global coordinates of a point in the <i>point</i> string, and the coordinates of the BCAM's kinematic mounting balls with the <i>mount</i> string. It returns the global coordinates of the point.</p>

<pre>
lwdaq global_from_bcam_point "0 1 0" "0 1 0 -1 1 -1 1 1 -1"
0.000000 2.000000 0.000000
</pre>

<p>See <a href="bcam_from_global_point"</a> for more details. For a description of the BCAM coordinate system, and how it is defined with respect to a BCAM's kinematic mounting balls, consult the BCAM <a href="http://alignment.hep.brandeis.edu/Devices/BCAM/User_Manual.html">User Manual</a>.</p>
}
		if (argc<>4) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq global_from_bcam_point point mount".');
			exit;
		end;
		Tcl_SetReturnShortString(interp,
			string_from_xyz(
				global_from_bcam_point(
					xyz_from_string(Tcl_ObjShortString(argv[2])),
					kinematic_mount_from_string(Tcl_ObjShortString(argv[3])))));
	end 
	else if option='bcam_source_bearing' then begin
{
<p>Calculates the line upon which a light source must lie for its image to be centered at <i>spot_center</i>. The line is returned as a string containing six numbers. The first three numbers are the coordinates of the BCAM pivot point in BCAM coordinates in millimeters. The last three numbers are a unit vector in the direction of the line. The BCAM itself you describe with its calibration constants in the <i>camera</i> string. The <i>camera</i> string contains nine elements, as described in the <a href="http://alignment.hep.brandeis.edu/Devices/BCAM/User_Manual.html">BCAM User Manual</a>. The <i>camera</i> string specifies length in millimeters and rotation in milliradians.</p>

<pre>
lwdaq bcam_source_bearing "1.72 1.22" "P0001 1 0 0 0 0 1 75 0"
1.000000 0.000000 0.000000 0.000000 0.000000 1.000000
</pre>

<p>Note that the first element in the <i>camera</i> string is the name of the camera, even though this calculation does not use the camera name. In the example above, P0001 is the camera name, the pivot point is at (1,0,0) in BCAM coordinates, the camera axis is parallel to the BCAM <i>z</i>-axis,  the pivot point is 75 mm from the lens, and the CCD rotation is zero. We transform point (1.72, 1.22)  on the CCD (dimensions are millimeters) into a bearing that passes through the pivot point (1,0,0) in the direction (0,0,1). The point (1.72,1.22) is our aribitrarily-chosen center of the CCD in all currently-available BCAMs (it is close to the center of the TC255P image sensor, but not exactly at the center). The BCAM camera axis is the line passing through the CCD center and the pivot point.</p>
}
		if (argc<>4) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq bcam_source_bearing spot_center camera".');
			exit;
		end;
		Tcl_SetReturnShortString(interp,
			string_from_xyz_line(
				bcam_source_bearing(
					xy_from_string(Tcl_ObjShortString(argv[2])),
					bcam_camera_from_string(Tcl_ObjShortString(argv[3])))));
	end 
	else if option='bcam_source_position' then begin
{
<p>Calculates the BCAM coordinates of a light source whose image is centered at <i>spot_center</i>, and which we know to lie in the plane <i>z</i> = <i>bcam_z</i> in BCAM coordinates. The routine is similar to <a href="#bcam_source_breagin">bcam_source_bearing</a>, but you specify the BCAM <i>z</i>-coordinate of the source as well, in millimeters. The routine determines the position of the source by calling <a href="#bcam_source_breagin">bcam_source_bearing</a> and intersecting the source bearing with the <i>z</i>=<i>range</i> plane.</p>

<pre>
lwdaq bcam_source_position "1.72 1.22" 1000 "P0001 1 0 0 0 0 1 75 0"
1.000000 0.000000 1000.000000
</pre>

<p>Here we see the source is at (1,0,1000) in BCAM coordinates, where all three coordinates are in millimeters. You specify the BCAM itself with its calibration constants using the <i>camera</i> string, just as for <a href="#bcam_source_bearing">bcam_source_bearing</a>.</p>
}
		if (argc<>5) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq bcam_source_position spot_center bcam_z camera".');
			exit;
		end;
		Tcl_SetReturnShortString(interp,
			string_from_xyz(
				bcam_source_position(
					xy_from_string(Tcl_ObjShortString(argv[2])),
					real_from_string(Tcl_ObjShortString(argv[3])),
					bcam_camera_from_string(Tcl_ObjShortString(argv[4])))));
	end 
	else if option='straight_line_fit' then begin
{
<p>Fits a straight line to <i>data</i>, where <i>data</i> contains a string of numbers, alternating between <i>x</i> and <i>y</i> coordinates. The routine returns a string of three numbers: slope, intercept, and rms residual. The rms residual is the standard deviation of the difference between the straight line and the data, in the <i>y</i>-direction. The data "0 3 1 5 2 7 5 13" would represent a straight line with slope 2, intercept 3, and rms residual 0. The result would be "2.000000 3.000000 0.000000".</p>
}
		if (argc<>3) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq straight_line_fit data".');
			exit;
		end;
		lsp:=Tcl_ObjLongString(argv[2]);
		gp:=read_xy_graph(lsp^);
		straight_line_fit(gp,slope ,intercept,rms_residual);
		dispose_xy_graph(gp);
		writestr(lsp^,slope:fsr:fsd,' ',intercept:fsr:fsd,' ',rms_residual:fsr:fsd);
		lwdaq_long_string:=lsp^;
		dispose_long_string(lsp);
		Tcl_SetReturnLongString(interp,lwdaq_long_string);
	end 
	else if option='ave_stdev' then begin
{
<p>Calculates the average and standard deviation of <i>data</i>, where <i>data</i> contains a string of numbers. The routine returns the average and standard deviation separated by a space.</p>
}
		if (argc<>3) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq ave_stdev data".');
			exit;
		end;
		lsp:=Tcl_ObjLongString(argv[2]);
		gpx:=read_x_graph(lsp^);
		writestr(lsp^,average_x_graph(gpx):fsr:fsd,' ',stdev_x_graph(gpx):fsr:fsd);
		dispose_x_graph(gpx);
		lwdaq_long_string:=lsp^;
		dispose_long_string(lsp);
		Tcl_SetReturnLongString(interp,lwdaq_long_string);
	end 
	else if option='linear_interpolate' then begin
{
<p>Interpolates between the two-dimensional points of <i>x_y_data</i> to obtain an estimate of <i>y</i> at <i>x</i>=<i>x_position</i>. If you pass "2.5" for the x position, and "0 0 10 10" for the x-y data, the routine will return "2.500000".</p>
}
		if (argc<>4) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq linear_interpolate x_position x_y_data".');
			exit;
		end;
		position:=real_from_string(Tcl_ObjShortString(argv[2]));
		lsp:=Tcl_ObjLongString(argv[3]);
		gp:=read_xy_graph(lsp^);
		linear_interpolate(gp,position,interpolation);
		dispose_xy_graph(gp);
		writestr(lsp^,interpolation:fsr:fsd);
		lwdaq_long_string:=lsp^;
		dispose_long_string(lsp);
		Tcl_SetReturnLongString(interp,lwdaq_long_string);
	end 
	else if option='sum_sinusoids' then begin
{
<p>Adds two sinusoidal waves of the same frequency together. You specify the two waves with their amlitude and phase. The phase must be in radians. The amplitude is dimensionless. The result contains the amplitude and phase of the sum of the two waves. If you pass the numbers "1 0 1 0.1" to the routine, it will return "1.997500 0.050000".</p>
}
		if (argc<>6) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq sum_sinusoids a.amplitude a.phase b.amplitude b.phase".');
			exit;
		end;
		a.amplitude:=real_from_string(Tcl_ObjShortString(argv[2]));
		a.phase:=real_from_string(Tcl_ObjShortString(argv[3]));
		b.amplitude:=real_from_string(Tcl_ObjShortString(argv[4]));
		b.phase:=real_from_string(Tcl_ObjShortString(argv[5]));
		a:=sum_sinusoids(a,b);
		writestr(s,a.amplitude:fsr:fsd,' ',a.phase:fsr:fsd,' ');
		Tcl_SetReturnShortString(interp,s);
	end 
	else if option='fourier_term' then begin
{
<p>Calculates a term in the discrete Fourier transform of <i>waveform</i> by calling <i>calculate_ft_term</i> from <a href="http://alignment.hep.brandeis.edu/Software/Sources/utils.pas">utls.pas</a>. You specify the waveform as a string of real numbers. Each represents the value of the waveform at discrete, consecutive moments in time (or space, or some other one-dimensional metric) separated by the sample interval. You specify which term you want to calculate by giving its period in units of sample intervals with the <i>period</i> parameter. The routine returns a string containg the amplitude and phase of the term in the fourier transform corresponding to <i>period</i>. If you pass "2" for the period and "0 1 0 1 0 1" for the data, the routine returns, "1.000000 0.500000". The phase, as you can see, is given in units of sample interval, and its sine is such that you subtract it from the phase of a sinusoid to create the Fourier term. To obtain the zero-frequency (DC) term, which corresponds to period infinity, pass period "0" to the routine. We use "0" as a code for "infinity", since we cannot calculate the discrete fourier transform at period zero. If you want to calculate a fourier transform made up of many fourier terms, try using the fourier_transform routine instead. It is much faster when you have a large waveform and many frequencies in your desired spectrum, because it translates the TCL <i>data</i> string into a Pascal binary string only once.</p>
}
		if (argc<>4) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq fourier_term period waveform".');
			exit;
		end;
		period:=real_from_string(Tcl_ObjShortString(argv[2]));
		lsp:=Tcl_ObjLongString(argv[3]);
		gpx:=read_x_graph(lsp^);
		calculate_ft_term(period,gpx,amplitude,offset);
		dispose_x_graph(gpx);
		writestr(lsp^,amplitude:fsr:fsd,' ',offset:fsr:fsd);
		lwdaq_long_string:=lsp^;
		dispose_long_string(lsp);
		Tcl_SetReturnLongString(interp,lwdaq_long_string);
	end 
	else if option='fourier_transform' then begin
{
<p>Calculates a series of terms in the discrete Fourier transform of <i>waveform</i>. In effect, this option acts like repeated calls to fourier_term, but is more efficient for large waveform strings, because TCL does not have to copy the string for each term. Instead of passing the routine a single period for a single fourier term, you pass a list of periods. The routine returns a list of terms, each term consisting of the period, amplitude and offset, separated by spaces. To improve its accuracy when calculating terms with non-zero frequency, the routine subtracts the average value of the waveform from each term in the waveform before it calculates terms. In response to period "0", the routine returns the average value of the waveform, so "0" is how you indicate period infinity.</p>
}
		if (argc<>4) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq fourier_transform periods waveform".');
			exit;
		end;
		lsp:=Tcl_ObjLongString(argv[2]);
		periods:=read_x_graph(lsp^);
		dispose_long_string(lsp);
		lsp:=Tcl_ObjLongString(argv[3]);
		gpx:=read_x_graph(lsp^);
		average:=average_x_graph(gpx);
		for i:=0 to gpx^.num_points-1 do gpx^[i]:=gpx^[i]-average;
		lsp^:='';
		for i:=0 to periods^.num_points-1 do begin
			if (periods^[i] = 0) then 
				writestr(lsp^,lsp^,0.0:fsr:fsd,' ',average:fsr:fsd,' ',0.0:fsr:fsd,eol)
			else begin
				calculate_ft_term(periods^[i],gpx,amplitude,offset);
				writestr(lsp^,lsp^,periods^[i]:fsr:fsd,' ',amplitude:fsr:fsd,' ',offset:fsr:fsd,eol);
			end;
		end;
		dispose_x_graph(gpx);
		dispose_x_graph(periods);
		lwdaq_long_string:=lsp^;
		dispose_long_string(lsp);
		Tcl_SetReturnLongString(interp,lwdaq_long_string);
	end 
	else if option='matrix_inverse' then begin
{
<p>Calculates the inverse of a square matrix. You pass the original matrix as a string of real numbers in <i>matrix</i>. The first number should be the top-left element in the matrix, the second number should be the element immediately to the right of the top-left element, and so on, proceeding from left to right, and then downwards to the bottom-right element. The command deduces the dimensions of the matrix from the number of elements, which must be an integer square. For more information about the matrix inverter, see matrix_inverse in utils.pas. The "lwdaq matrix_inverse" routine is inefficient in its use of the matrix_inverse function. The routine spends most of its time translating between TCL strings and Pascal floating point numbers. A 3x3 matrix inversion with random elements takes 1000 us on our 1 GHz iBook, of which only 8 us (0.1% of its time) is spend calculating the inverse. A 40x40 matrix takes 100 ms on the same computer, and spends 4.5 ms (4% of its time) calculating the inverse. The routine returns the inverse as a string of real numbers, in the same format as the original <i>matrix</i>.</p>
}
		if (argc<>3) then begin
			Tcl_SetReturnShortString(interp,'Wrong number of arguments, should be '
				+'"lwdaq matrix_inverse matrix".');
			exit;
		end;
		lsp:=Tcl_ObjLongString(argv[2]);
		M:=read_matrix(lsp^,0,0);
		N:=matrix_inverse(M);
		lsp^:='';
		write_matrix(lsp^,N);
		dispose_matrix(M);
		dispose_matrix(N);
		lwdaq_long_string:=lsp^;
		dispose_long_string(lsp);
		Tcl_SetReturnLongString(interp,lwdaq_long_string);
	end
	else begin
		Tcl_SetReturnShortString(interp,'Bad option "'+option+'", must be one of '
			+'"bcam_from_global_point global_from_bcam_point'
			+' bcam_source_bearing bcam_source_position'
			+' straight_line_fit sum_sinusoids linear_interpolate'
			+' fourier_term fourier_transform matrix_inverse".');
		exit;
	end;
	
	if error_string<>'' then Tcl_SetReturnShortString(interp,error_string);
	lwdaq:=Tcl_OK;
end;

{
	lwdaq_init initializes the pascal run-time system, sets the initial values
	of all variables declared in this program and all its units, and installs
	the lwdaq commands in the tcl interpreter.
}
function lwdaq_init(interp:pointer):integer;
	attribute (name='Lwdaq_Init');

var
	p:pointer;
		
begin
{
	We try to initialize the TCL and TK stub libraries if USE_TCL_STUBS is defined,
	as it might be by a compiler option -DUSE_TCL_STUBS.
}
{$ifdef USE_TCL_STUBS}
	p:=tcl_initstubs(interp,'8.1',0);
	if (p=nil) then begin
		lwdaq_init:=Tcl_Error;
		exit;
	end;
	p:=tk_initstubs(interp,'8.1',0);
	if (p=nil) then begin
		lwdaq_init:=Tcl_Error;
		exit;
	end;
{$endif}
		
	initialize_pascal(0,nil,nil);
	initialize_main;	 
	
	gui_interp_ptr:=interp;
	gui_draw:=lwdaq_gui_draw;
	gui_writeln:=lwdaq_gui_writeln;
	gui_wait:=lwdaq_gui_wait;
	gui_support:=lwdaq_gui_support;
	
	p:=tcl_createobjcommand(interp,'lwdaq',lwdaq,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_config',lwdaq_config,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_draw',lwdaq_draw,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_graph',lwdaq_graph,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_image_create',lwdaq_image_create,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_image_contents',lwdaq_image_contents,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_image_characteristics',lwdaq_image_characteristics,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_image_histogram',lwdaq_image_histogram,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_image_exists',lwdaq_image_exists,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_image_results',lwdaq_image_results,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_image_destroy',lwdaq_image_destroy,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_image_manipulate',lwdaq_image_manipulate,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_data_manipulate',lwdaq_data_manipulate,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_photo_contents',lwdaq_photo_contents,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_rasnik',lwdaq_rasnik,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_rasnik_shift',lwdaq_rasnik_shift,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_wps',lwdaq_wps,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_bcam',lwdaq_bcam,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_diagnostic',lwdaq_diagnostic,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_gauge',lwdaq_gauge,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_flowmeter',lwdaq_flowmeter,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_voltmeter',lwdaq_voltmeter,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_rfpm',lwdaq_rfpm,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_inclinometer',lwdaq_inclinometer,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_recorder',lwdaq_recorder,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_calibration',lwdaq_calibration,0,nil);
	p:=tcl_createobjcommand(interp,'lwdaq_sampler',lwdaq_sampler,0,nil);
	lwdaq_init:=tcl_pkgprovide(interp,package_name,version_num);
end;

{
	lwdaq_unload deletes the above commands from the interpreter.
}
function lwdaq_unload(interp:pointer;flags:integer):integer;
	attribute (name='Lwdaq_Unload');

begin
	lwdaq_unload:=Tcl_Error;
end;

{
	lwdaq_safeinit returns an error because we don't have a 
	safe version of the initialization.
}
function lwdaq_safeinit(interp:pointer):integer;
	attribute (name='Lwdaq_SafeInit');

begin
	lwdaq_safeinit:=Tcl_Error;
end;

{
	lwdaq_safeunload returns an error because we don't have a
	safe version of the unload.
}
function lwdaq_safeunload(interp:pointer;flags:integer):integer;
	attribute (name='Lwdaq_SafeUnload');

begin
	lwdaq_safeunload:=Tcl_Error;
end;

{
		The main part of the program we never use. See the comments at the top.
}
begin
end.
