9 The Linux Lab project and other links
Max runs on each LINUX machine with X11R6. I used to test it on a 486/133MHz PC with 32MB memory. The minimum speed of the machine depends mainly on the type of data acquisition process and measurement speed. Ususally a Pentium PC with at least 133MHz is recommended for a good performance of the GUI. If you take data with a frequency of 10Hz and only plot the data every few seconds, speed is no problem. For higher speeds a faster machine is required. Especially if Client and Server run on the same host(which is usually the case). Since Max relies on polling as the main data-acquistion process, it needs a lot of computing power for speeds > 100Hz, in order not to loose trigger events. A Pentium II with > 233 MHz should definetely do it, though. If you want to use KDE as your graphical fronted, don´t forget that it takes a lot of computing power and memory. I recommend at least 64 MB and a P233 or higher in that case, otherwise you may run into trouble when taking larger datasets. If you run the polld on a seperate host and use the networking capabilities of MAX, a small PC is enough. But you have to have an X-Server running before you start the polld. (This will be improved in later versions) For a really good performance of Max also for an application,which is triggered around 50Hz, I recommend a PII233MHz and 64MB RAM+120MB SWAP.
Realtime Applications :
If realtime data acquisition is required, the data acquistion
routine must be seperated from the device-routine in the polld. It
has to run independently of Max in the background being triggered by an
externel SIGNAl or interrupt.
An application could look like the following :
The <initialization> part of the device routine starts the
dataacqusition routine in the background, which sits there and waits for
the interrupts. Then the main-device routine in the polld just sits
there and waits for a SIGNAL (it may also try to read from a pipe)
from the dataq-routine, to read out the data is has acquired in the meantime.
The Data exchange may take place via sockets, pipes or, which is best in
that case, shared memory segments with an IPC via semaphores.
The data is read out by the device function and stored as an array.
Software :
I have started to develop Max on a LINUX system with a kernel version
higher than 2.0 and I have used gcc ver 2.7.2 to compile it. There are
additional requirements for Max ver 2.0 : You can also compile it with
egcs. I use the rpm package egcs-981208-1. There is a flag in the Makefile
which you have to set according to the compiler you want to use. Check
out the Makefile for details.
Max uses classes from the STL (Standard
Template library). That means you have to have installed the appropriate
package. I use : libstdc++-2.8.0-14. Some distributions do not provide
this package. If you use SUSE 6.0, as I do, get the package from the fileserver
and install it with rpm -i package_name. To check wether the package is
installed on your system, do a rpm -qa | grep libstd.You have to
use Qt ver 1.42 or higher !
This version comes without any hardware drivers and only with dummy
device modules, which generate artificial data for test purposes. Max was
designed to be independent of any hardware drivers or interface cards,
it does not perform any communication with any hardware or driver. Only
the device -functions are supposed to access the hardware for data acqusition
and measurement control. Hence, you can install any driver by including
the appropritate filenames in the Makefile. We currently do use Max with
the LINUX-Lab project GPIB driver and the Wiener CC16 CAMAC driver. If
you want to use these, you can get a version with these drivers included
together with the device modules already written for it from our ftp site.
Hence, before you can use Max, download Qt from the adress given above. Do what they tell you there to install it (It's very easy) and make shure that you have the right paths in the max makefile (I use /usr/local/qt/include on Linux and /software/qt in the Institute's network ). (On a local system You should also install qt in /usr/local/qt.)
2) Do a "gunzip max20.tar.gz" and untar it with the command : tar -xvf max20.tar.gz. This will create a new folder called Max20 in the directory you have copied the .tar file into.
3) Read the README file
4) Check that you have the Qt libs installed in /usr/local/qt and that the $QTDIR variable is set to this value. For more information about how set the paths can be found in the installation READMEs of Qt.
5) Check which compiler(s) is (are) installed on your system. Then set the compiler flag in the Makefile accordingly. It is _GCC_ for the GNU-C Compiler and _EGCS_ for egcs. Also make shure the libstdc++ package is installed.
6) Type "make clean", "make depend" and "make". That should do it to compile the demo version without any hardware drivers.
7) To include device drivers, check out the hardware section of this manual.
8) To test it, just type "max". This will start the client and the server on your localhost. If everything works, you should see the Main Window.
9) Errors : Sometimes you may get something like : "Couldn't find shared libraries libqt.so.1". This means, that your LD_LIBRARY_PATH variable is not set to the path, where the Qt-libs are found. Set it accordingly. Usually the path is set in the resource files in the homedirectory. Either in .cshrc, .tcshrc (in case you use the c-shell or the tc-shell) or .bshrc. The command īls -alī shows you all directory entries. There is an instruction about how to set the paths in the installation README of qt.
For detailed information on the parts of the source code, which are relevant to the user, please check out the Class member list of the Source code browser.
Check out the README file in the programm directory. It lists all the
files of the packet. The files consists out of two sets. One for the Client,
one for the Server.
3.2 What happens during a measurement ?
How does Max perform a measurement ?
It has been said in the introduction, that the measurement is performed
by calling a set of C-functions in a certain order, the priority. Furthermore
it has been mentioned, that each "device" is represented by four functions.
After the measurement is started, Max calls these device function periodically
for each channel-number until the maximum number of integrations is reached.
The function (or class), that performs the function calls is called "scheduler".
(see the the C-Class browser for details
on the function calls of the Scheduler class). The image on the right illustrates
this timing cycle. Each color (red,blue and green) stands for one loop
in the scheduler routine. The red color indicates a loop over the priorities
(e.g. the device index), the blue color indictaes a loop over the integrations
and the green color stands for the last, most important, loop over the
channel numbers.
If the users presses start, max sets channel and integrations to zero and priority to 1. It then takes a look in the priority database to see what device has the current priority 1.
Before the actual measurement starts, the _init functions of all devices are called first in the order of their priorities. These device routines prepare the measurement, they do not take any data to store it. They simply reset some devices or do similar stuff. Never save any data in the _init routine.
The pre-trigger routines are called during each cycle of integrations. (blue line in the graph) Hence, each time a datapoint is taken.They are used to reset each device again, before the main device function is called.
The actual device functions, that perform the meausurement, are called in the same cycle of integrations but after the pre-trigger functions. These are the functions that can take data and hand in over to Max with the command void Scheduler::store(type ). The physical data saved with the see the void Scheduler::store(type) command is buffered and integrated automatically.
After the maximum number of integrations is reached, the channel number is increased, integrations is set to zero again and n the cycle starts again until the stop button is pressed or the maximum channel number is reached. The device functions can always interrupt the measurement by returning FALSE to the scheduler.
After the measurement is finished, the _close functions are called in the order given by their priorities, to close up all devices.
As has been pointed out in the previous section, each device function consists out of four functions. The _init, the _pre, the _main and the _close functions. These functions have to be listed in the vector of functions pointers in the file device.h.
There is no limitation in channel numbers or integrations,. But be careful : Max does not check for a lack of memory !. Use the "top" command to check on memory usage.
Max can handle as many devices functions as you like. The devices database can have any size,since max handles all data storage in dynamically allocated linked lists.
The number of different data sets, that can be "stored" per channel is limited to 100 so far, but that number can be changed as well in the file : definitions.hpp (Check out the Source Code Browser for more information)
3.4 The format of the saved data file
The Save-Widgets, that pops up after the measurement is finished differentiates between "scalar" data, these are single integers or doubles, "Vector"-data, these are vectors and Images (Have not fully implemented that yet).
Scalar data is saved to disk in two files in ASCII format. One is the
.dat
file, wich contains the data and one is a .txt file, which
contains the dvice information and some additional text.The scalar data
file contains rows and columns. The rows follow the channels number and
there is a column for each variable stored in the experiment. That means,
for example, if you store three values per channel and measure 1000 channels,
the data file will contain 3 columns and 1000 rows. and looks as follows
: (the text in italics is not saved, of course)
channel | 1st variable | 2nd variable | 3rd variable |
1 | 112 | 1212 | 1212 |
2 | 23 | 23 | 2323 |
3 | 23223 | 2323 | 2323 |
4 | 2323 | 2323 | 2323 |
5 | 2323 | 2323 | 2323 |
The variables are seperated by a space character, so the file can be read with most data visualization programs like PAW, and ORIGIN or gnuplot and Xmgr.
Array data is stored in ASCII format in a file with the extender .str
as well together with a .txt file. The format of the arrays is a little
bit different. Arrays are stored in a Matrix. The follwoing example shows
the result of a measurement with a device that has "stored" 2 arrays per
channel and measured 3 channels. This time, the first column is saved as
index
!
index | 1st array | 2nd array | 1st array | 2nd array | 1nd array | 2nd array |
1 | a11 | a21 | a11 | a21 | a11 | a21 |
2 | a12 | a22 | a12 | a22 | a12 | a22 |
3 | a13 | a23 | a13 | a23 | a13 | a23 |
The Main initfile initmax :
###### Main Initfile for Max ##### # this file contains some initialization parameters for the programm # start max with your own initfile by providing # a new initfilename as start parameter # type max -f filename, # you personal initfile then contains your personal device combination for your # individual experiment integrations 1 channels 2 dev_file initfile pr_file priority #data_file data # The data filename has the formThe above listing shows a typical initfile. It contins the parameters integrations and Channels, which are most important for the program. Additionaly you find a list of filenames. These are the filenames of the other two initialization files, priority and initfile and the default filenames of the files to save the measured data in. On line is commented out with a "#" sign. This means that Max does not use a standard filename for this filetype, the data file. For more information of save-filenames see the Section "Save Data". You can start Max with the option "-f" to read in another file, than the deafult initfile named initfile._<"data_file">_ .dat array_file array #Name of file to contain strings and arrays data_dir ./ #data-directory path image_file im
The Priority file : priority : This file contains a list of all devices, which are used in the programm :
# # priority file for max (7/98) # # this file contains the device priorities # the priorities give the order in which the devices are called # all devices in the device vector (in devices.h) # MUST have an entry here as well !! # (and they have to be in the same order) # zero means that the device is switched off # this file is read at startup # it's name is given in the main max-initfile "initmax" # see also the files : devices.h and laser.cpp laser 2 # this is a dummy device for test purposes adc 1 # dito scope 0 # the priority of the device "scope"This file contains a list of all devices included in the programm together with a number, the so called priority. This number indicates in which order the devices are called during the measurement. In our example there are three devices included in the programm. (Included means, that there exist the C-Routines that form the device module and that they are compiled into the programm. Check out the "Getting Started-Section" for information on how to include new devices.) The device "adc" is called first, secondly the device "laser" is called, the device "scope" will not be called at all, it's priority is zero. There must be an entry here for each, that is listed in the device.h device-description header. Also the order in which the devices are given must be the same. The main initfile initmax The file described in the following is very important and the user will propably modifiy it the most. The initmax file contains a description off all device-parameters used by the device-modules. Max uses this file to initialize it's internal database with it. The user-defined device functions can access this database with the putvar and getvar functions to read device parameters for initialization purposes or to store data for the next run. The contents of this database are shown in the left listbox in the Main window. The user can modify this data by double clicking on the appropriate line in the listbox. The following listing shows a small excerpt from the initfile belonging to the device combination given in the priority-file above :
######################################################################## # example devices initfile for Max ver 2.x # August. 98 # by Christian Rosen, rosen@mbi-berlin.de ############################################################################## # this file contains all device parameters that a device routine can access # these variables can be accessed on line by the user as well # # a new device declaration starts with the word device # active means that the device is called during all integrations cycles # store specifies a new data_set definition # if the device wants to store measured data, it HAS TO HAVE a store statement # the plot-options are optional, for default values see the file : definitions.hpp # Don't forget the PLOT and ENABLED flags. # PLOT = TRUE -> data is plotted # ENABLE is checked by your device before data is stored # You can select the data channels with it # # see also the files : # devices.h (-> the device vector and the device routine declarations are here) # laser.cpp (-> here are all the device routines ) # priority (-> the order of the devices is given here ) # definitions.hpp (-> default definitions for plot windows ) # ############################################################################### device laser active double stepwidth 300 store channel1 anything between store and the "begin" sign is discarded ! { #this is a comment PLOT TRUE #the PLOT and the ENABLED flag ENABLED TRUE #default is FALSE AUTOSCALE TRUE #Plot Window options } double phase 1 double steps 0.11 int ende 55 int scale 10 store channel_2 { P_MIN_SIZE_X 100 ENABLED FALSE #this enables the chan AUTOSCALE TRUE PLOT TRUE VGRIDLINES TRUE HGRIDLINES FALSE AUTOSCALE TRUE Y_UPPER_LIMIT 10 } double anfang 0.001 device adc active store channel_1 { PLOT TRUE ENABLED FALSE AUTOSCALE TRUE } store channel2 { PLOT TRUE ENABLED FALSE AUTOSCALE TRUE } store channel3 { PLOT TRUE ENABLED TRUE AUTOSCALE TRUE Y_UPPER_LIMIT 500 } int gate 100 int gate2 140 # fill in comments where you likeThis file contains two devices, "laser and "adc". Let's go line by line through the initfile given above : There is one section in this file for each "device". It starts with the word DEVICE and a device name. Anything that follows this statement will be associated with this device until a new DEVICE declaration is inserted. This line is followed by the word active. This command declares the device "laser" to an active device. That means that it is called during all cycles of integrations. If a device is not active, it is only called during the first integration. This is useful for a device that does not take data but simply sets some values. (A Voltage supply or a laser, that is scanned only once before the program integrates over several shots of the laser) Hence, each device, that stores values, e.g. takes data, has to be an active device.
Each device function takes the same arguments : List *device_data, Scheduler *parameters.These are pointers to the classes that handle the data storage. Just copy this line in your function.
The device_data -> getvar (&var, var_name, device_name) and
device_data-> putvar(value,var_name,device_name) functions handle the device Parameter data exchange with the List class. The parameters are simply device and variable name as specified in the initfile and a reference to the value (getvar) or simply the value itself(putvar)
The parameters-> store(var_name) function, stores measured data in the database.
The int parameters-> enabled(varname,device_name) function, checks wether this channel is enabled. If it returns TRUE, the value is stored.
#include "list.hpp" //this has to be included #include "sched.hpp" //this has to be included #include "error.hpp" //this has to be included//!The initfunction is called once before the measurement starts int adc_init(List *device_data, Scheduler *parameters) { printf("adc_init\n"); return TRUE; } //!this is the Pre_initfunction, it's called each time, before the integrastions cycle starts int adc_pre(List *device_data, Scheduler *parameters) { printf("adc_pre\n"); return TRUE; } //! This is the main function of the \b device \b module adc. Here the data is taken. int adc(List *device_data, Scheduler *parameters) { int x; double messwert; double gate; double gate2; int enabled=1; printf("\nadc called "); result = device_data->getvar(&gate,"adc","gate"); //read a value from the database into the variable gate enabled = device_data->enabled("adc","channel1"); //check if channel1 is enabled if(enabled) { printf("adc chan 1 is enabled \n"); gate +=0.01; messwert = 1*sin(gate); result = device_data->putvar(gate,"adc","gate"); parameters -> store(messwert); } enabled = device_data->enabled("adc","channel2"); //check if channel is enabled if (enabled) { result = device_data -> getvar(&gate2,"adc","gate2"); messwert = cos(gate2); gate2 += 0.01; result = device_data -> putvar(gate2,"adc","gate2"); parameters -> store(messwert); } enabled = device_data-> enabled("adc","channel3"); //check if channel3 is enabled if (enabled) { ind++; parameters -> store(ind); } return 1; } //! Close your device after the measurement is finished int adc_close(List *device_data, Scheduler *parameters) { printf("adc close.................\n"); return TRUE; } /************************************************************* * ERROR COMMANDS : * int Scheduler::error (char *formatstr,....) * -> compose your individual errormessage * int Scheduler::error (char *ERRMSG,char *NAME) * -> uses standard error messages from error.hpp * they return TRUE if the user wants to continue * they return FALSE in case of abort * -> abort the measurement by returning FALSE from the device_function *************************************************************/Take a look at the Source Code Browser for more information about the Scheduler class and the commands accesible by the device function.
int laser_init (List *, Scheduler *); //header declarations int laser (List *, Scheduler *); int laser_pre (List *, Scheduler *); int laser_close(List *, Scheduler *); /**************************************** *NUMBER_DEVICES contains the number of *devices in the Function_List vector *MAKE SHURE IT IS CORRECT !!!!! *****************************************/ #define NUMBER_DEVICES 3 //increase this value if you add a new device !! int (*Function_List [NUMBER_DEVICES]) (List *, Scheduler *) = {laser,adc,scope}; int (*Pre_Function_List [NUMBER_DEVICES]) (List *, Scheduler *) = {laser_pre,adc_pre,scope_pre}; int (*Init_Function_List [NUMBER_DEVICES]) (List *, Scheduler *) = {laser_init,adc_init,scope_init}; int (*Close_Function_List[NUMBER_DEVICES]) (List *, Scheduler *) = {laser_close,adc_close,scope_close};Again we use a file from the example. Hence it contains descriptions about the three devices "scope","adc" and "laser". The first four lines are one set of header declarations. Remember, each device consists out four routines! The definition NUMBER_DEVICES simply gives the length of the following device vector. Increase this number if you add a new device. The Function_List-Vector is a vector to a pointer of functions, the device functions. These are functions that receive a pointer to the List and Scheduler class as arguments and return an integer. To implement a new device, you have to add the names of the new device functions to this vector.
4.4 How to implement new device functions
To implement a new device, you have to the following :
- LINUX HQ, for Kernel Hackers but it also contains a lot of good documentation
- The LINUX journal LINUX resources...
- The LINUX documentation project and a lot of links...
Comments to :