Back: 9.2 GUI-fying the script Forward: 9.4 Hahn-echo detected EPR script   FastBack: 9. Example EDL Scripts Up: 9. Example EDL Scripts FastForward: 10. Command Line Options         Top: fsc2 Contents: Table of Contents Index: Index About: About This Document

9.3 cw-EPR monitor script

Before doing a real experiment one often needs to optimize the experimental parameters like phase, amplification etc. In these cases it would be rather inconvenient to have start an experiment in order to check e.g the signal intensity, adjust the parameters and start another test experiment. One rather would have a program where it's possible to adjust the parameters while scanning the field region where the signal is to be expected. This can be done easily with a another EDL script. Here we also will make use of the built-in methods to create additional graphical elements like buttons, input fields etc.

Before starting to write such a script let's collect the requirements:

Now here is the complete EDL script:

 
  1 DEVICES:
  2 
  3 er032m;       // Bruker field controller
  4 sr530;        // SR 530 lock-in amplifier
  5 
  6 
  7 VARIABLES:
  8 
  9 field;
 10 field_step = 0.5 G
 11 data[ 2 ];
 12 
 13 last_field;
 14 last_field_step = field_step;
 15 new_field, new_field_step;
 16 
 17 max_field = -50.0 G
 18 min_field = 23 kG;
 19 min_field_step = 1 mG;
 20 max_field_step = 100 G;
 21 
 22 I;
 23 
 24 Sweep_State = 0;   // 0: stopped, 1: up, -1: down
 25 Pause_State = 1;   // 0: running, 1: paused
 26 
 27 Sweep_Up, Sweep_Down, Sweep_Stop,
 28 Field_In, Field_Step_In, Field_Out,
 29 Pause, Clear;
 30 
 31 
 32 PREPARATIONS:
 33 
 34 init_1d( 2, "Points", "Signal [uV]" );
 35 
 36 
 37 EXPERIMENT:
 38 
 39 lockin_lock_keyboard( "OFF" );
 40 field = magnet_field( field );
 41 last_field = field;
 42 
 43 hide_toolbox( "ON" );
 44 Field_Out = output_create( "FLOAT_OUTPUT", field,
 45                            "Current field (in G)" );
 46 Sweep_Up   = button_create( "RADIO_BUTTON", "Sweep up" );
 47 Sweep_Stop = button_create( "RADIO_BUTTON", Sweep_Up, "Stop sweep" );
 48 Sweep_Down = button_create( "RADIO_BUTTON", Sweep_Up, "Sweep Down" );
 49 button_state( Sweep_Stop, "ON" );
 50 
 51 Field_In = input_create( "FLOAT_INPUT", field,
 52                          "Set a new field (in G)" );
 53 Field_Step_In = input_create( "FLOAT_INPUT", field_step,
 54                               "Set a new field step (in G)" );
 55 Pause = button_create( "PUSH_BUTTON", "Pause display" );
 56 button_state( Pause, Pause_State );
 57 Clear = button_create( "NORMAL_BUTTON", "Clear screen" );
 58 hide_toolbox( "OFF" );
 59 
 60 I = 1;
 61 
 62 FOREVER {
 63 
 64   wait( lockin_time_constant( );
 65 
 66   IF Pause_State == 0
 67   {
 68     data = lockin_get_data( 1, 2 );
 69     display_1d( I, data[ 1 ] / 1 uV, 1,
 70                 I, data[ 2 ] / 1 uV, 2 );
 71     I += 1;
 72   } 
 73 
 74   IF button_state( Sweep_Up ) {
 75     IF Sweep_State != 1 {
 76       Sweep_State = 1;
 77       draw_marker( I, "RED" );
 78     }
 79   } ELSE IF button_state( Sweep_Down ) {
 80     IF Sweep_State != -1 {
 81       Sweep_State = -1;
 82       draw_marker( I, "GREEN" );
 83     }
 84   } ELSE {
 85     IF Sweep_State != 0 {
 86       Sweep_State = 0;
 87       draw_marker( I, "YELLOW" );
 88     }
 89   }
 90 
 91   IF Sweep_State == 1 {
 92      IF field + field_step <= max_field {
 93        field = magnet_field( field + field_step );
 94        output_value( Field_Out, field );
 95      } ELSE {
 96        Sweep_State = 0;
 97        button_state( Sweep_Stop, 1 );
 98        draw_marker( I, "YELLOW" );
 99      }
100    } ELSE IF Sweep_State == -1 {
101      IF field - field_step >= min_field {
102        field = magnet_field( field - field_step );
103        output_value( Field_Out, field );       
104      } ELSE {
105        Sweep_State = 0;
106        button_state( Sweep_Stop, 1 );
107        draw_marker( I, "YELLOW" );
108      }
109   }
110 
111   new_field = input_value( Field_In );
112   IF new_field != last_field {
113     IF ( new_field >= min_field AND new_field <= max_field {
114       field = magnet_field( new_field );
115       output_field( Field_Out, field );
116       last_field = new_field;
117     } ELSE {
118       input_field( Field_In, last_field );
119     }
120   }
121 
122   new_field_step = input_value( Field_Step_In );
123   IF new_field_step != last_field_step {
124     IF new_field_step >= min_field_step AND
125        new_field_step <= max_field_step {
126       field_step = new_field_step;
127       last_field_step = new_field_step;
128     } ELSE {
129       input_field( Field_Step_In, last_field_step );
130     }
131   }
132 
133   Pause_State = button_state( Pause );
134 
135   IF button_state( Clear ) {
136     clear_curve( );
137     clear_marker( );
138     rescale( 64 );
139     I = 1;
140   }
141 }

I hope that this script doesn't look too daunting, but most of its length is due to checking for and reacting to user input, the really interesting content being rather small as you will see.

The DEVICES section again is very simple, we're just loading the modules for the Bruker field controller and the SR530 lock-in amplifier. Since this lock-in has two channels we will be able to display both the x- and the y-phase signal which might help to set the correct phase for the real experiment.

The VARIABLES section has gotten rather long. But the most important variables are just the first three for the current field, the current field step size and an array with two elements for storing the x- and y-phase signal.

The next four variables at line 13 to 15 are needed in the evaluation of the input fields for setting a new field and field step size. We will discuss their meaning later.

The variables defined on line 17 to 20 for the minimum and maximum field and field step size are not strictly necessary. We also could have hard-coded the values into the script (but at the cost of readability). It's also going to be easier to adapt the script for using a different field controller with a different set of limits when such values can be found in just one place instead of having them scattered all over the script.

The variable I is just a counter variable which will tell us at which coordinate to draw the next measured data points.

Sweep_State and Pause_State (line 24 and 25) are for remembering the current state the program is in. If Sweep_State is set to 1 the program is currently sweeping the field to higher values, if it's set to 0 the field sweep is stopped, and if it is -1 we're sweeping down. If Pause_State is set to 0 we're supposed to measure and display new data, if it's 1 data acquistion is disabled.

The final variables declared in the lines 27 to 29 are integer variables for storing handles for the graphical elements like buttons and in- and output fields. To e.g. determine if a button has been pressed we will need its handle.

The PREPARATIONS section is very short, all which is done here is the initialization of the display: we need two curves for the x- and y-phase signal and we set the labels for the x- and y-axis. We can't know in advance how many points we're going to have to display, so the corresponding parameter in the function call has been left out. Since it is rather likely that while running the script the field will not be swept in just one direction (and in fixed-sized steps) but will be swept up and down (or even stay at the same value for longer times) in unpredictable ways it's not possible to draw a reasonable x-axis scale, so also the parameters for defining the scale are omitted, resulting in just point numbers getting displayed at the x-axis.

Now we're getting to the most interesting part, the EXPERIMENT section. The first thing we do (line 39) is to re-enable the lock-in amplifiers keyboard. Normally, all keys of devices get disabled when an experiment starts, so we have to re-enable them if we want to manually change the settings.

The second thing to be done is figuring out at which field the magnet currently is and store it in both the variables field and last_field. Because we didn't call the function magnet_setup() in the PREPARATIONS section as in the previous example script the magnet will simply start of at the field value it has been set to manually.

All the lines 43 to 58 are for setting up an additional window with graphical elements, i.e. buttons and in- and output fields. The first element is an output field, i.e. a box to just display values. It is created by calling the function output_create() with three arguments. The first is the type of the field where "FLOAT_OUTPUT" stands for a field for displaying floating point values. The second is the value to display in the field and the third is a label to appear below the field. Obviously, this first field is for displaying the current magnetic field. The integer value returned by the function is later going to used when we have to refer to the field for updating the displayed value when the magnet gets swept.

The following 4 lines (46-49) are for creating a set of radio buttons. Radio buttons always appear in groups and the important property is that only one of them can be switched on at a time. That's exactly what we need to control if the magnet is to be swept up or down or stopped - it can only be in one of these states. To create a button we need to call the function button_create() For the first of group of radio buttons it takes just two arguments, the first one indicating the type of the button, i.e. "RADIO_BUTTON" (there are also other types of buttons as we will see soon) and a label to be drawn to the right of the button.

For the other two radio buttons belonging to the group we need an additional argument to state to which group they belong to. This extra argument is simply the number of the first button of the group. It has to be given between the buttons type and the label string.

After creating all three buttons we now select one of them as activated with a call to button_state(). When we want to set the state of a button we have to pass the function two arguments, the number associated with the button and the state, either "ON" or "OFF" (but 1 and 0 will also do). If we don't set one of the buttons of a group of radio buttons the first of the group will be selected per default. But since we better start off with the sweep being stopped we here have to manually set the corresponding button.

Next we add two input fields where the user can enter a new field as well as a new field step size. This is down by a call of the function input_create(). Since both values are floating point numbers we use "FLOAT_INPUT" as the first argument. As already in the case of the function for creating an output field, the second argument is the value to be shown at first in the input field and the third argument is a label string.

Finally two further buttons are created. The first one is for stopping and restarting the acquisition of data. This button must be either active or inactive, i.e. it's either pushed in or not. The natural choice in this case is to use a button of type "PUSH_BUTTON" which has exactly these properties. As we already have seen when creating radio buttons, the function to create a button is button_create() and the first argument passed to it must be the type of the button. The second argument is just the label string to be shown on its right side. Since push buttons start of in the inactive state per default but it is probably better not to start the experiment already acquiring data (the user probably first will have to set a reasonable field value) we have to activate button (indicating that the acquisition is stopped) by calling button_state() with the buttons handle and the new state to be set.

The last button for clearing the display is just a normal button, i.e. a button that just can be clicked on. This is indicated by the button type "NORMAL_BUTTON" as the first argument to button_create().

The creation of all the buttons and in- and output fields is enclosed by two calls of the function hide_toolbox() (line 43 and 58). Before we start creating the buttons etc. this disables the immediate display of the newly created objects in a window labled "Toolbox", redrawing the window for each object. When we're done with adding new objects we "un-hide" the window and it willl now be visible with all the new buttons etc. at once. Actually, this isn't necessary, it's just cosmetics to make the creation of the objects look nicer. The script will also work when you leave out both the lines.

Just before entering the main loop for the experiment we set the counter variable I, which will be used as the x-coordinate when drawing new data points, to 1, i.e. to represent the leftmost point in the display area.

After all these preparations the real fun starts. Since we can't know in advance how many points the user is going to measure be use a FOREVER loop, i.e. a loop that will run until the user hits the Stop button. All of the test experiment will happen within this loop.

The first action within the loop is to wait for the lock-in amplifiers time constant. In contrast to the previous script we don't determine the lock-ins time constant once at the start of the experiment but ask the lock-in each time we have to wait. This is necessary because the keyboard of the lock-in is unlocked, so the user can change the time constant whenever he likes, so we have to make sure that we always get the correct time constant.

As you will notice, we also wait even when the acquisition is stopped. This is reasonable because otherwise loop would be repeated extremely fast in paused mode, uselessly eating up computer time.

Unless the acquistion hasn't been stopped (i.e. as long as Pause_State is 0) we now ask the lock-in amplifier for new data. To be able to display both the x- and y-phase signal we tell it to return data for both channel 1 and 2. Of course, it must return 2 values, which it stores in an array with two elements, data. Please note that not all lock-in amplifiers have two channels, for these we would have to change the script a bit to fetch and display only one data point.

After having gotten both the new measured data points we draw them in the display at x-coordinate I and afterwards we increment I to be prepared for the next set of data points.

Before now also stepping the field up or down we first have to check the state of the sweep buttons - the user may have pushed one of them. This is what happens in the lines 74 to 89. Checking the state of a button is done using the same function that already was used to set the state of a button, button_state(), but without a second argument, just with the button handle as the only argument. If a radio or push button is active it returns a non-zero number, otherwise 0.

The first of a series of tests is for the sweep up button. If it is active but we're not sweeping up (i.e. when Sweep_State isn't 1) we'll have to change the Sweep_State variable to in accordance with the users request. To also give a visual indication that the sweep directon changed we then draw a marker at the current coordinate. This is one via the draw_marker() function. It takes two arguments, the x-coordinate and a string for the color of the marker, and will draw a vertical dashed line in the selected color.

Of course, if the sweep-up button isn't active we also have to check the sweep-down and the stop-sweep buttons, in exactly the same way as we did for the sweep-up button. When we're done we know into which direction we have to sweep the field (or if we shouldn't do a field step at all).

This brings us to the lines 91 to 109. Here the field is swept (unless the stop-sweep button is active). But we have to be a bit careful: before stepping up or down the field we have to make sure that the new field value is still within the allowed limits for the magnet. (If we don't take care here the script might be stopped by the driver for the magnet - but most drivers will handle the situation more gracefully by simply refusing to sweep the field out of the allowed limits, but you shouldn't count on this.)

If the new field value is still within the allowed range we tell the magnet to set the new field by calling the function magnet_field() with the field to be set as the argument. It returns the value of the new field, which we store in the field variable. When done with this we still have to update the output field for displaying the current field by calling the output_value() function. It takes two arguments, the handle of the output field we got when it was created and the new value to be displayed.

But if the new field is not within the magnets limits we have to automatically stop the sweep and give the user a visual feedback. This is done by setting the Sweep_State variable to 0, indicating a stopped sweep. By setting the variable we avoid that the next time we run through the loop the program again will try to change the field. And to notify the user about this we also activate the button the user would push for stopping a sweep and draw a marker in the same color that would be used in this case.

But we're not finished yet. In the mean time the user might have entered a new field or field step value in one of the input fields. To be able to figure this out the last known value in these entry fields had been stored in the variables last_field and last_field_step. To check if something has changed we need to figure out the current values in the input fields and compare them to our stored version. If something changed we need to take appropriate action. This is what happens during lines 111 to 130.

For both the field and the field step size we fetch the current value by calling input_value() with just one argument, the entry fields handle, and store the returned value in a variable, new_field and new_field_step, respectively. Now we can compare the last recoded value to the current value of the input field. If they are identical nothing neeeds to be done.

But if they should differ we shouldn't blindly set a new field or field step size but instead first check if the user supplied value is reasonable. So we have to compare it to the minimum and maximum field or field step size. If e.g. the new field value is within the allowed range we can tell the magnet to move to the requested field and store it in both the variables field and last_field. Of course, we also have the output field showing the current field. On the other hand, if the value isn't acceptable we must notify the user which we do by resetting the input field to its previous value, using again the input_value() function.

Now we're nearly done, there only remain two more buttons to be taken care of. Handling the button for stopping the acquisition is simple - we just have to get its state and set the Pause_State variable accordingly, which is done in line 133.

Also dealing with the button for clearing the display isn't complicated. First we must determine if it had been clicked on in the mean time. Again, we can use the button_state() function. It will return a non-zero value if the button has been clicked on since the last invocation of the function for this button, otherwise 0. (Actually, the value returned by the function is the number of times the button was clicked on since the last call.)

If the user clicked onto the button we have to clear both the curves and also remove the markers that might have been drawn, using the functions clear_curve() and clear_marker() (if invoked without an argument the functions remove all curves and markers, otherwise you would have to supply a list of curve numbers and marker handles). But we also call the function rescale() to reduce the x-scaling to some reasonable value (here 64) - if we wouldn't do so and the number of points displayed before removing the curves was very large, e.g. 10000, the x-scaling would remain set for displaying 10000 points, which would probably be rather not what the user expects.

Finally, we also have to set the counter variable I to 1 in order to have the next measured value drawn at the leftmost position of the display (instead to the right of the deleted curve).


Back: 9.2 GUI-fying the script Forward: 9.4 Hahn-echo detected EPR script   FastBack: 9. Example EDL Scripts Up: 9. Example EDL Scripts FastForward: 10. Command Line Options

This document was generated by Jens Thoms Toerring on September 6, 2017 using texi2html 1.82.