Back: 14.4.8 Numerical conversions and comparisons Forward: 14.5.1 Pulse Functions   FastBack: 14. Writing Modules Up: 14. Writing Modules FastForward: A. Installation         Top: fsc2 Contents: Table of Contents Index: Index About: About This Document

14.5 Writing modules for pulsers

Modules for pulsers are a bit more difficult to write than drivers for other devices. The reason is that pulsers play a rather important role in modern spectrometers and thus setting the pulses should be made as easy as possible for the user. Of course, it would be possible to deal with pulsers in exactly the same way as normal devices, i.e. to define just a set of functions for setting different pulse properties etc. but this would make the programs much harder to write and understand. Instead many aspects of dealing with pulses and pulsers are integrated directly into the EDL language. While this makes it easier for the user writing EDL scripts it requires more work by the writer of the module for a pulsers.

If, for example, the user defines a new pulse in the PREPARATIONS section the relevant part of the EDL file will similar look to this:

 
P3: FUNCTION = MW,
    START    = P1.START + 200 ns,
    LENGTH   = 140 ns;

When fsc2 finds these lines it will have to call several functions that must be defined within the module. First it needs to call a function that allows it to inform the module that there is a new pulse numbered 3. Next it will inform the module that the pulse function the new pulse 3 is associated with is the function for microwave pulses. Next it detects that the start position of the new pulse is defined in terms of the values that (hopefully) have been already set for the pulse 1 and must ask the module for the start position of pulse 1. Using the returned value fsc2 now can calculate the start position of the new pulse 3 and must then call another function in the module to tell the module about this position. Finally, another function in the module must exist so that fsc2 can inform it about the length of the new pulse. Using these (and a lot more) functions the module will be able to set up an internal representation of the pulser state and to bring the pulser into this state at the start of the experiment.

To make it possible to integrate handling of pulsers in this way directly into EDL the module for a pulser must obviously define quite a lot of non-EDL functions and some additional variables. Pointers to all of these needed functions are collected in one structure, Pulser_Struct:

 
struct {
    const char * name;
    bool needs_phase_pulses;
    bool has_pods;

    bool ( * assign_channel_to_function )( int function, long channel );
    bool ( * assign_function )( int function, long connector );
    bool ( * set_function_high_level )( int function, double high_voltage );
    bool ( * set_function_low_level )( int function, double low_voltage );
    bool ( * invert_function )( int function );
    bool ( * set_function_delay )( int function, double delay );

    bool ( * set_timebase )( double timebase );
    bool ( * set_timebase_level )( int level );

    bool ( * set_trigger_mode )( int mode );
    bool ( * set_repeat_time )( double rep_time );
    bool ( * set_trig_in_level )( double voltage );
    bool ( * set_trig_in_slope )( int slope );
    bool ( * set_trig_in_impedance )( int state );

    bool ( * set_phase_reference )( int phase, int function );

    bool ( * phase_setup_prep )( int func, int type, int pod, long val );
    bool ( * phase_setup )( int func );

    bool ( * new_pulse )( long pulse_number );
    bool ( * set_pulse_function )( long pulse_number, int function );
    bool ( * set_pulse_position )( long pulse_number, double ptime );
    bool ( * set_pulse_length )( long pulse_number, double ptime );
    bool ( * set_pulse_position_change )( long pulse_number, double ptime );
    bool ( * set_pulse_length_change )( long pulse_number, double ptime );
    bool ( * set_pulse_phase_cycle )( long pulse_number, long cycle );

    bool ( * get_pulse_function )( long pulse_number, int * function );
    bool ( * get_pulse_position )( long pulse_number, double * ptime );
    bool ( * get_pulse_length )( long pulse_number, double * ptime );
    bool ( * get_pulse_position_change )( long pulse_number, double * ptime );
    bool ( * get_pulse_length_change )( long pulse_number, double * ptime );
    bool ( * get_pulse_phase_cycle )( long pulse_number, long * cycle );

    long ( * ch_to_num )( long channel );

    /* The following entries are deprecated and exist for backward
       compatibility only */

    bool ( * set_max_seq_len )( double seq_len );
    bool ( * keep_all_pulses )( void );
    bool ( * set_phase_switch_delay )( int function, double del_time );
    bool ( * set_grace_period )( double gp_time );
} Pulser_Struct;

At the start all the pointers in this structure are set to NULL (fsc2 has still no idea which functions it actually has to call), name is also a NULL pointer and the boolean variable needs_phase_pulses is set to false. Now, when the init_hook of the pulser module gets run it has to fill in values for all the function pointers it supplies functions for - only this will allow fsc2 to figure out where the relevant functions to call are. When the module does not define a function it must leave the corresponding entry in the structure unchanged, i.e. leave it a NULL pointer. Most of the following text will try to explain in detail what the different functions are supposed to do and the meaning of the arguments of the functions.

But first the three variables to be set will be discussed. The first variable, name, is simply the name of the pulser that will be used in error messages etc. When setting this variable within the init_hook function it should first be checked if it is still a NULL pointer. If not the module should print an error message and quit immediately - when name is not NULL a different pulser module has already been loaded and currently it is not possible to deal with more than one pulser.

The second variable, needs_phase_pulses, must be set to a true value only if the experiment the pulser is connected to has phase switches that need their own pulses and if the module is prepared to create these phase pulses automatically. Currently, this is only the case for the Frankfurt S-band spectrometer.

The third variable, has_pods, must be set when the pulser has internal channels for storing pulse sequences which get mapped to certain output connectors (pods), like the Sony/Tektronix DG2020. Per default the variable is set to false.


Back: 14.5 Writing modules for pulsers Forward: A. Installation   FastBack: 14. Writing Modules Up: 14.5 Writing modules for pulsers FastForward: A. Installation         Top: fsc2 Contents: Table of Contents Index: Index About: About This Document

14.5.1 Pulse Functions

Now follows a list of all functions that can be defined within a pulser module and advertised to fsc2 by assigning a pointer to the function in the pulsers structure. All functions are supposed to return a boolean value. Please remember that not all functions must exist, if they don't exist and you don't supply a pointer for some of the functions in the pulser structure fsc2 will tell the user automatically that the ability associated with the function is not available with the driver. You can be sure that all time values that these function receive are integer multiples of one nanosecond.

With the exception of the functions for setting pulse properties

 
set_pulse_position()
set_pulse_length()
set_pulse_position_change()
set_pulse_length_change()

and the functions for asking pulse properties, i.e. the functions with names starting with get_pulse_, all functions will only be called before the experiment is started, i.e. in the time between the calls of the init_hook() and the test_hook() function.

During the test run. i.e. between the test_hook() and the exp_hook() function call (while the global variable TEST_RUN is set) functions to change pulse positions and lengths will be called. In this functions the internal representation of the pulser state has to be updated and the consistency of the state has to be checked (i.e. do the pulses stay separated, don't they overtake each other, do the lengths remain larger than zero, do the positions stay larger than zero and don't exceed the maximum channel length, etc.). The driver also might choose to store the longest duration of a pulse sequence during the test run to be used later in the calculation of the padding needed to set a fixed repeat time for the experiment.

`bool assign_channel_to_function(int function, long channel)'

This function is called when in the ASSIGNMENTS section in the description of a pulse function the CHANNEL (or CH) keyword is found, i.e.

 
ASSIGNMENTS:
MW: CH = 1, ....

There are two types of pulsers, pulsers (like the Sony/Tektronix DG2020 that have some internal channels, that can be freely assigned to output connector (and for which the variable needs_phase_pulses in the pulser structure must be set), and pulsers (like the Tektronix HFS9000 that just have output channels. For the first type of pulsers this function is called to assign a pulse function to one of the internal channels (and not the output connector, this is what the next function is for, see below), while for the second type of pulsers th function is for assigning a pulse function to one of the output connectors.

As discussed in the chapter about pulsers (see Channel setup) there are currently 15 different types of pulse functions. To avoid having to change your module in case the numbering of the functions changes you should only use symbolic names for functions. These symbolic names are defined in `src/global.h':

 
enum {
    PULSER_CHANNEL_MW = 0,
    PULSER_CHANNEL_TWT,
    PULSER_CHANNEL_TWT_GATE,
    PULSER_CHANNEL_DET,
    PULSER_CHANNEL_DET_GATE,
    PULSER_CHANNEL_DEFENSE,
    PULSER_CHANNEL_RF,
    PULSER_CHANNEL_RF_GATE,
    PULSER_CHANNEL_PULSE_SHAPE,
    PULSER_CHANNEL_PHASE_1,
    PULSER_CHANNEL_PHASE_2,
    PULSER_CHANNEL_OTHER_1,
    PULSER_CHANNEL_OTHER_2,
    PULSER_CHANNEL_OTHER_3,
    PULSER_CHANNEL_OTHER_4
};

You also better don't rely on the number of pulse functions, instead of using a hardcoded value of 15 use instead the PULSER_CHANNEL_NUM_FUNC.

Beside the definition of pulse function numbers there is also an array with the full names for the functions (to be used in error messages).

 
const char * Function_Names[ ] = {
    "MW", "TWT", "TWT_GATE","DETECTION", "DETECTION_GATE",
    "DEFENSE", "RF", "RF_GATE", "PULSE_SHAPE", "PHASE_1",
    "PHASE_2", "OTHER_1", "OTHER_2", "OTHER_3", "OTHER_4" };

The functions PULSER_CHANNEL_PHASE_1 and PULSER_CHANNEL_PHASE_2 are a bit different from the rest because these functions are reserved for automatically created phase pulses, so the user should never be able to create pulses with both these functions.

The second argument of assign_channel_to_function() obviously is either the number of channel (for pulsers with no pods) or the output connector (for pulsers with pods). Please note that for pulsers of the first type several channels may be assigned to one function (e.g. when automatically created phase pulses are used, i.e. the variable needs_phase_pulses is set).

`bool assign_function(int function, long connector)'

For pulsers of the second type, i.e. pulsers that have internal channels and independent output pods this function is used to associate a pulse function to one of the output functions, i.e. if in the function description the POD keyword is found:

 
ASSIGNMENTS:
  MW: POD = 3, ...

Only one function should be assignable to an output connector.

`bool set_function_high_level(int function, double high_voltage)'

This function is called to set the high voltage level to be output for a certain pulse function, i.e. when the V_HIGH keyword is found in the function description. Obviously, high_voltage is the voltage to be used for the high voltage level in Volts.

`bool set_function_low_level(int function, double low_voltage)'

This function is called to set the low voltage level to be output for a certain pulse function, i.e. when the V_LOW keyword is found in the function description. To stay compatible with other pulser modules I would recommend not to accept low voltage levels that are actually higher than the high voltage level and to tell the user to use the function invert_function() instead.

`bool invert_function(int function)'

This function is called to tell the pulser module that the output for a certain pulse function has to be reversed polarity, i.e. that a high voltage is to be output for a pulse off state while a high voltage has to be output while a pulse with this function is switched on. The function is automatically called for the INV or INVERT keyword in the pulse function description.

`bool set_function_delay(int function, double delay)'

This function is called to tell the pulser to use a delay for one of the pulse function, i.e. when the DELAY keyword is found in the description of a function. When possible you should be prepared also to accept negative delays (which of course requires that the pulser is triggered internally).

`bool set_timebase(double timebase)'

This function gets called when the TIMEBASE keyword is found in the ASSIGNMENTS section, i.e.

 
ASSIGNMENTS:
  TIMEBASE: 10 ns;

You will have to check that this is an acceptable timebase value and you will also have to check later that all pulse positions, lengths etc. are integer multiples of this timebase.

`bool set_timebase_level(int level)'

This function gets called when the TIMEBASE keyword is found in the ASSIGNMENTS section, followed by either TTL or ECL, i.e.

 
ASSIGNMENTS:
  TIMEBASE: 16 ns, TTL;

This indicates that the timebase is derived from external input with either TTL or ECL levels. The argument the function gets passed is an integer, either TTL_LEVEL or ECL_LEVEL.

`bool set_trigger_mode(int mode)'

This function gets called when a trigger mode description is found in the assignments section, i.e.

 
ASSIGNMENTS:
  TRIGGER_MODE: EXTERNAL;

There are two possible values for the trigger mode, either external or internal. In `src/global.h' an enumeration is already defined for the values of mode with EXTERNAL (set to 1) and INTERNAL (set to 0). In order to avoid problems if the values should ever get changed you should use these predefined values.

`bool set_repeat_time(double rep_time)'

The function gets called for setting the repetition time or frequency of an pulsed experiment, i.e. when either the REPETITION_TIME or REPETITION_FREQUENCY keyword is found in the trigger mode description in the ASSIGNMENTS section. Obviously, to be able to set a repetition time or frequency the trigger mode must be internal and the module has to check that this is the case.

`bool set_trig_in_level(double voltage)'

This function is called when the LEVEL keyword is found in the trigger mode description in the ASSIGNMENTS section to set the trigger level for external trigger mode. The module should tell the user that setting a trigger level doesn't make sense in the case that the user specified internal trigger mode.

`bool set_trig_in_slope(int slope)'

This function is called when the SLOPE keyword is found in the trigger mode description in the ASSIGNMENTS section to set the trigger slope for external trigger mode. In `src/global.h' an enumeration is already defined for the values of slope with POSITIVE (for triggering on the trigger input signal crossing the trigger level coming from a lower voltage and set to 1) and NEGATIVE (set to 0). In order to avoid problems if the values should ever get changed you should use these predefined values. The module should tell the user that setting a trigger slope doesn't make sense in the case that the user specified internal trigger mode.

`bool set_trig_in_impedance(int state)'

This function gets called to set the input impedance of the trigger input channel when the IMPEDANCE keyword is found in the trigger mode description in the ASSIGNMENTS section. In `src/global.h' an enumeration is already defined for the values of state with HIGH (set to 1) and LOW (set to 0). In order to avoid problems if the values should ever get changed you should use these predefined values - this could be the case when a pulser has to be integrated that has more than two different trigger input impedances. The module should tell the user that setting a trigger input impedance doesn't make sense in the case that the user specified internal trigger mode.

`bool set_phase_reference(int phase, int function)'

This again a function that gets called under somewhat different circumstances, depending on how the pulser module is supposed to work. If you are writing a module that has to create phase pulses automatically (and thus you have set the variable needs_phase_pulses in the pulser structure), this function will be called when in the definition of a phase function the function the phase pulses will be used with is set. That means if the EDL script contains a line like

 
ASSIGNMENTS:
  PHASE_2: MICROWAVE, POD = 2, 3, ....

Obviously, this is meant for a pulser module that automatically creates phase pulses (otherwise the use of a phase function would not be allowed) and this statement is intended to tell the module that the function PHASE_2 (with its pulses appearing on the output connectors 2 and) is to be used to create phase pulses for the microwave pulses. To tell the module the function set_phase_reference() gets called with the number of the phase function (i.e. PULSER_CHANNEL_PHASE_1 or PULSER_CHANNEL_PHASE_1) as the first argument and the number of the pulse function (in the example the MICROWAVE phase function) as the second argument.

In contrast, for modules that don't have to create phase pulses this function is called from within the ASSIGNMENTS section when lines of the form

 
ASSIGNMENTS:
  PHASE_SETUP_2:  MICROWAVE, ...

are found. In this case MICROWAVE is again the function that is to be phase cycled and is passed to the function as the second argument. The first argument is either 0 or 1, depending on if this pertains to the first phase cycled function (i.e. when the command in EDL script starts with either PHASE_SETUP or PASE_SETUP_1) or the second phase cycled function (i.e. for PHASE_SETUP_2).

`bool phase_setup_prep(int func, int type, int pod, long val)'

Again this function is called under slightly different circumstances, i.e. depending on if you have set the variable needs_phase_pulses in the pulser structure (meaning that phase pulses have to be created) or not. In the first case a syntax of

 
PHASE_1:  MICROWAVE, POD = 2, 3;

PHASE_SETUP_1:  +X: POD_1 = ON, POD2 = OFF,
                +Y: POD1  = ON, POD_2 = 1,
                -X: POD2 = 0, 0,
                -Y: 1, 1;

is expected in the EDL script. The first line indicates that the phase function 1 is to be used to control phase pulses for microwave pulses and the output pods to be used are the pods 2 and 3. The following lines are supposed to tell the module that in order to create a +X phase pulse the first output pod set in the definition of the first phase function (PHASE_1) (i.e. pod 2) must be in the high state, while the second pod (i.e. pod 3) must be low.

For this kind of phase setup the function phase_setup_prep() will be called exactly 8 times in a row, 2 times for each phase type (i.e. +X, +Y, -X and -Y). It will be called always with the first argument set to 0 to indicate that PHASE_SETUP_1 is currently done (where PHASE_SETUP without a number is just a short form for this), in for PHASE_SETUP_2 the first parameter would be 1.

The second argument is type of phase, in `src/global.h' an enumeration defining PHASE_PLUS_X for a phase of +X, PHASE_PLUS_Y for +Y, PHASE_MINUS_X for -X and PHASE_MINUS_Y for -Y is defined and should be used in your module. There is also a PHASE_CW pseudo phase type defined in case your module has to support a special cw-mode configuration.

The third argument is the output pod to use, where 0 stands for the first pod defined previously for the phase function, i.e. in our example a 0 would represent the pod numbered 2. In contrast, an argument of 1 indicates the second output pod, i.e. in our example the pod numbered 3. Finally, you also have to expect an argument of -1, meaning "the first of the two pods" if none of both the pods has been set yet for this phase type or "the other one" if already one one the two pods has been set.

The fourth and final argument is the state of the output for the put when the pulse is output. In our example this means that it will be 1 when a 1 or ON is found in the EDL script and 0 for OFF or 0.

Accordingly, the EDL code for the phase setup given above will lead to the following sequence of calls of the function phase_setup_prep():

 
/* PHASE_SETUP_1:  +X: POD_1 = ON, POD2 = OFF, */

phase_setup_prep( 0, PHASE_PLUS_X, 0, 1 );   
phase_setup_prep( 0, PHASE_PLUS_X, 1, 0 );

/* +Y: POD1  = ON, POD_2 = 1, */

phase_setup_prep( 0, PHASE_PLUS_Y, 0, 1 );   
phase_setup_prep( 0, PHASE_PLUS_Y, 1, 1 );

/* -X: POD2 = 0, 0, */

phase_setup_prep( 0, PHASE_MINUS_X, 1, 0 );   
phase_setup_prep( 0, PHASE_MINUS_X, -1, 0 );

/* -Y: 1, 1; */

phase_setup_prep( 0, PHASE_MINUS_Y, -1, 1 );   
phase_setup_prep( 0, PHASE_MINUS_Y, -1, 1 );

If you are writing a module that does not create phase pulses (and you accordingly set the variable needs_phase_pulses in the pulser structure to false) the phase setup command looks a bit different:

 
PHASE_SETUP_1:  MICROWAVE,
              +X: POD = 1,
              +Y: POD = 2,
              -X: POD = 4,
              -Y: 5,
              CW: 3;

For these EDL code the function phase_setup_prep() gets called five times in a row. Again the first and second parameter the function will receive is the phase setup number (i.e. either 0 or 1, for our example it would be 0 because we're dealing with the first phase setup), and the second is the phase type (i.e. PHASE_PLUS_X, PHASE_PLUS_Y, PHASE_MINUS_X, PHASE_MINUS_Y and PHASE_CW). The third parameter has in this case no meaning at all and its value should be discarded. The fourth and final is the output pod or channel to be used for a pulse with the current phase. Thus, the function would be called in the following sequence (without any other intervening calls except possibly set_phase_reference()):

 
phase_setup_prep( 0, PHASE_PLUS_X, (discard), 1 );   
phase_setup_prep( 0, PHASE_PLUS_Y, (discard), 2 );
phase_setup_prep( 0, PHASE_MINUS_X, (discard), 1 );   
phase_setup_prep( 0, PHASE_MINUS_Y, (discard), 2 );
phase_setup_prep( 0, PHASE_CW, (discard), 1 );   

Here (discard) stands for an arbitrary value that has to be discarded.

`bool phase_setup(int func)'

This function is called to tell the module that a phase setup sequence is finished and no further commands of phase_setup_prep() for the phase setup with number function (i.e. either 0 for the first phase setup or 1 for the second) should happen. The module can now do some sanity checks on the data it received from the previous phase_setup_prep() calls or whatever else it needs to do.

`bool new_pulse(long pulse_number)'

This function is called when a new pulse definition is found in the PREPARATIONS section, i.e. for lines starting like this

 
  PULSE_13: ...

For this the function will called (with 13 as the pulse_number argument) to tell the module that there's a new pulse to be dealt with. Pulse numbers are always non-negative, so negative pulse numbers can be used for pulses generated internally by the module.

`bool set_pulse_function(long pulse_number, int function)'

This function is called when the function of a new pulse is set in the PREPARATIONS section, i.e. for

 
  PULSE_13:   FUNCTION = MICROWAVE, ...

The first argument is the pulse number (you can be sure that the function new_pulse() will have been called before with this pulse number as argument) and the pulse function number as the second argument (see the discussion of pulse function numbers above in the description of the function assign_channel_to_function()). If you don't have a god reason to do otherwise I would recommend to allow neither PULSER_CHANNEL_PHASE_1 nor PULSER_CHANNEL_PHASE_2 as pulse functions because these functions are usually reserved for internally generated phase pulses.

`bool set_pulse_position(long pulse_number, double ptime)'

This is the function that gets called to tell the module about the start position of a pulse, i.e. for lines like

 
  PULSE_13: START = 100 ns, ...

within the PREPARATIONS section or when during the experiment the position of a pulse is changed directly by assigning a new start position i.e. for lines like

 
  P13.START = 260 ns;

If you need different handling of both situations you can assign a new function pointer to the corresponding structure entry at the start of the EXPERIMENT section, for example in the exp_hook function (or at any other time it is convenient).

As in the case of the set_pulse_function() function the first argument is the pulse number, the second the start position of the pulse in seconds (but guaranteed to be an integer multiple of 1 ns, you still will have to check if it's not negative and also that it isn't an integer multiple of the pulsers timebase).

`bool set_pulse_length(long pulse_number, double ptime)'

The function gets called when the length of a pulse is set in the EDL file, i.e. for lines like

 
  PULSE_13: ...
            LENGTH = 2 us, ...

within the PREPARATIONS section or, within the EXPERIMENT section, to directly change the length of a pulse, e.g.

 
  P13_LENGTH = 2.1 us;

If you need different handling of both situations you can assign a new function pointer to the corresponding structure entry whenever you want.

Obviously, the first argument to the function is the pulse number, the second is the new length of the pulse in seconds. You will have to check yourself within the module that the pulse length hasn't an invalid value. If the length of the pulse is zero you must treat the pulse as switched off for the time being.

`bool set_pulse_position_change(long pulse_number, double ptime)'

This function is used to tell the module about the start position change of a pulse (i.e. when a pulse definition line containing the DELTA_START keyword is found in the PREPARATIONS section or a new value is assigned to the DELTA_START value of a pulse during the experiment), with the first argument being the pulse number, the second the start position change for the pulse (which might be negative).

`bool set_pulse_length_change(long pulse_number, double ptime)'

This function is used to tell the module about the length change of a pulse (i.e. when a pulse definition line containing the DELTA_LENGTH keyword is found in the PREPARATIONS section or a new value is assigned to the DELTA_LENGTH value of a pulse during the experiment), with the first argument being the pulse number, the second the length change for the pulse.

`bool set_pulse_phase_cycle(long pulse_number, long cycle)'

This function is called to set the phase sequence to be used for phase cycling the pulse indexed by the first argument, i.e. when commands like

 
  PULSE_13: PHASE_CYCLE = PHASE_SEQUENCE_1, ...

are found in the PREPARATIONS section of the EDL file. The second argument is either the number 1 or 2, indicating one of the currently allowed two phase sequences. But to avoid the necessity of changes of the module it is probably a good idea to test this value within the function.

`bool get_pulse_function(long pulse_number, int * function)'

This function is called by fsc2 to find out about the function of a pulse from the module. If a pulse with the number passed to the function exists (if it doesn't the module should print out an error message and throw an exception), it should set the variable pointed to by the second argument to the number of the pulses function and return a true value. If no function has been set for the pulse an error message should be printed out and an exception has to be thrown.

`bool get_pulse_position(long pulse_number, double * ptime)'

This function is called by fsc2 to find out about the current position of a pulse (not including function delays) from the module. If a pulse with the number passed to the function exists (otherwise the module should print out an error message and throw an exception), it should set the variable pointed to by the second argument to the start position (in seconds) of the pulse. If no start position has been set for the pulse an error message should be printed out and an exception has to be thrown.

`bool get_pulse_length(long pulse_number, double * ptime)'

This function in the module is called to determine the current length of a pulse. If a pulse with the number passed as the first argument exists the variable the second argument points to has to be set to the length of the pulse (in seconds). If no length has been set for the pulse an error message should be printed out and an exception has to be thrown.

`bool get_pulse_position_change(long pulse_number, double * ptime)'

This function should return the current setting of the position change setting for the pulse indexed by the argument in the variable pointed to by the second argument. If no start position change vale has been set for the pulse an error message should be printed out and an exception has to be thrown.

`bool get_pulse_length_change(long pulse_number, double * ptime)'

This function should return the current setting of the length change setting for the pulse indexed by the argument in the variable pointed to by the second argument. If no length change value has been set for the pulse an error message should be printed out and an exception has to be thrown.

`bool get_pulse_phase_cycle(long pulse_number, long * cycle)'

The function should return the number of the phase sequence (i.e. 1 or 2) associated with the pulse associated with pulse with the number passed to the function as the first argument. If a phase sequence has been set for the pulse the number has to be written into the variable pointed to by cycle, otherwise (or if no pulse with the number of the first argument exists) an error message has to be printed out and an exception should be thrown.

`long ch_to_num(long channel)'

Different pulser modules have different internal numbering schemes for their channels. On the other hand, fsc2 does only know about all the possible names of channels (as defined in `global.h' and `global.c'). So for fsc2 being able to pass channel numbers to the pulser module with the channel numbers the module expects the pulser module must support this function. It gets a channel number in the "global" numbering system according to the definitions in `global.h' and `global.c' and has to translate this number into the internally used channel number. If no translation is possible the module should throw an exception and print out an error message, stating that it has no channel of the name Channel_Names[channel].

`bool set_max_seq_len(double seq_len)'

This function is called when the MAXIMUM_SEQUENCE_LENGTH is found in the ASSIGNMENTS section, i.e.

 
ASSIGNMENTS:
  MAXIMUM_SEQUENCE_LENGTH: 10 us;

If you determine the maximum length of the pulser pattern in your module during the test run the value you get can be plain wrong if the EDL script contains e.g. FOREVER loops, IF/ELSE constructs etc. because it is not possible to determine during the test run which branches of the EDL script will be run in the real experiment. The MAXIMUM_SEQUENCE_LENGTH command should allow the user to correct the possible wrong value when necessary.

The obvious question is why bother at all to determine the maximum length of the pulse pattern and not use instead the maximum pattern length all the time? The reason is that clearing the whole pattern at the start of the experiment for some pulsers can take a rather long time. E.g. for the Sony/Tektronix DG2020 for each of the internal channels that are going to be used more than 64 kB of data would have to be send to the pulser, probably taking several minutes, while in most experiments only a small fraction of the maximum pattern length is really needed.

The use of the keyword MAXIMUM_SEQUENCE_LENGTH is deprecated. Instead, the pulser module should supply, if necessary, a function pulser_maximum_sequence_length() that can be used in the PREPARATIONS section and has the same effect. Assigning anything else then NULL to the set_max_seq_len member of the pulser structure is only for backward compatibility.

`bool bool keep_all_pulses(void)'

This function is called when the KEEP_ALL_PULSES keyword is found in the ASSIGNMENTS section. If this function is called your module may not delete pulses that it found during the test run never to be used.

The question, of course, is why delete pulses at all that are never used and not keep them? The reason is that when doing phase cycling it can happen that for each additional pulse lots of channels in the digitizer are needed, even to the point that the number of channels is exceeded. Therefor, as a default, pulses that are found to be never used during the test run are removed (after printing out a message to the user).

The use of the keyword KEEP_ALL_PULSES is deprecated. Instead, the pulser module should supply, if necessary, a function pulser_keep_all_pulses() that can be used in the PREPARATIONS section and has the same effect. Assigning anything else then NULL to the keep_all_pulses member of the pulser structure is only for backward compatibility.

`bool set_phase_switch_delay(int function, double del_time)'

This function is called when in the ASSIGNMENTS section a line like

 
  PHASE_SWITCH_DELAY: 40 ns;

is found. The module must use the value of the second argument del_time as the time that pulses of the phase function indicated by the first argument (which currently can be only either 1 or 2) start before the 'real' pulses of the pulse function the phase function is associated with. Of course, if the module is not prepared to create phase pulses at all, this function does not need to exist and the corresponding entry in the pulsers structure should be left a NULL pointer. If no phase switch delay is set for a phase-cycled function it should (but that's not a necessity but just a recommendation) use a default value of 20 ns.

The use of the keyword PHASE_SWITCH_DELAY is deprecated. Instead, the pulser module should supply, if necessary, a function pulser_phase_switch_delay() that can be used in the PREPARATIONS section and has the same effect. Assigning anything else then NULL to the set_phase_switch_delay member of the pulser structure is only for backward compatibility.

`bool set_grace_period(double gp_time)'

This function is called when in the ASSIGNMENTS section a line like

 
  GRACE_PERIOD: 20 ns;

is found. The module must use the value of the second argument gp_time as the time that pulses of the phase function indicated by the first argument remain switched on after the 'real' pulses of the pulse function the phase function is associated with already ended. If these 'real' pulses get to near to each other to allow having both a phase switch delay as well as the 'grace period' the later may be reduced below the value set by the user. If the module is not prepared to create phase pulses at all, this function does not need to exist and the corresponding function pointer entry in the pulsers structure should remain a NULL pointer.

The use of the keyword GRACE_PERIOD is deprecated. Instead, the pulser module should supply, if necessary, a function pulser_grace_period() that can be used in the PREPARATIONS section and has the same effect. Assigning anything else then NULL to the set_grace_period member of the pulser structure is only for backward compatibility.


Back: 14.5 Writing modules for pulsers Forward: A. Installation   FastBack: 14. Writing Modules Up: 14.5 Writing modules for pulsers FastForward: A. Installation

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