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.
• Pulse Functions |
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 theCHANNEL
(orCH
) 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
andPULSER_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 variableneeds_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 functioninvert_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
orINVERT
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 theASSIGNMENTS
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 theASSIGNMENTS
section, followed by eitherTTL
orECL
, 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
orECL_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
withEXTERNAL
(set to1
) andINTERNAL
(set to0
). 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
orREPETITION_FREQUENCY
keyword is found in the trigger mode description in theASSIGNMENTS
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 theASSIGNMENTS
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 theASSIGNMENTS
section to set the trigger slope for external trigger mode. In `src/global.h' an enumeration is already defined for the values ofslope
withPOSITIVE
(for triggering on the trigger input signal crossing the trigger level coming from a lower voltage and set to1
) andNEGATIVE
(set to0
). 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 theASSIGNMENTS
section. In `src/global.h' an enumeration is already defined for the values ofstate
withHIGH
(set to1
) andLOW
(set to0
). 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 theEDL
script contains a line likeASSIGNMENTS: 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 functionset_phase_reference()
gets called with the number of the phase function (i.e.PULSER_CHANNEL_PHASE_1
orPULSER_CHANNEL_PHASE_1
) as the first argument and the number of the pulse function (in the example theMICROWAVE
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 formASSIGNMENTS: 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 either0
or1
, depending on if this pertains to the first phase cycled function (i.e. when the command inEDL
script starts with eitherPHASE_SETUP
orPASE_SETUP_1
) or the second phase cycled function (i.e. forPHASE_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 ofPHASE_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 to0
to indicate thatPHASE_SETUP_1
is currently done (wherePHASE_SETUP
without a number is just a short form for this), in forPHASE_SETUP_2
the first parameter would be1
.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
andPHASE_MINUS_Y
for-Y
is defined and should be used in your module. There is also aPHASE_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 a0
would represent the pod numbered 2. In contrast, an argument of1
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 a1
orON
is found in theEDL
script and0
forOFF
or0
.Accordingly, the
EDL
code for the phase setup given above will lead to the following sequence of calls of the functionphase_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 functionphase_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. either0
or1
, for our example it would be0
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
andPHASE_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 possiblyset_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 numberfunction
(i.e. either0
for the first phase setup or1
for the second) should happen. The module can now do some sanity checks on the data it received from the previousphase_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 thisPULSE_13: ...
For this the function will called (with
13
as thepulse_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. forPULSE_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 functionassign_channel_to_function()
). If you don't have a god reason to do otherwise I would recommend to allow neitherPULSER_CHANNEL_PHASE_1
norPULSER_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 likeP13.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 theexp_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 likePULSE_13: ... LENGTH = 2 us, ...
within the
PREPARATIONS
section or, within theEXPERIMENT
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 thePREPARATIONS
section or a new value is assigned to theDELTA_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 thePREPARATIONS
section or a new value is assigned to theDELTA_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 theEDL
file. The second argument is either the number1
or2
, 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
or2
) 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 bycycle
, 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 forfsc2
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 nameChannel_Names[channel]
.- `bool set_max_seq_len(double seq_len)'
This function is called when the
MAXIMUM_SEQUENCE_LENGTH
is found in theASSIGNMENTS
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 theEDL
script will be run in the real experiment. TheMAXIMUM_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 functionpulser_maximum_sequence_length()
that can be used in thePREPARATIONS
section and has the same effect. Assigning anything else thenNULL
to theset_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 theASSIGNMENTS
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 functionpulser_keep_all_pulses()
that can be used in thePREPARATIONS
section and has the same effect. Assigning anything else thenNULL
to thekeep_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 likePHASE_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 either1
or2
) 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 aNULL
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 functionpulser_phase_switch_delay()
that can be used in thePREPARATIONS
section and has the same effect. Assigning anything else thenNULL
to theset_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 likeGRACE_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 aNULL
pointer.The use of the keyword
GRACE_PERIOD
is deprecated. Instead, the pulser module should supply, if necessary, a functionpulser_grace_period()
that can be used in thePREPARATIONS
section and has the same effect. Assigning anything else thenNULL
to theset_grace_period
member of the pulser structure is only for backward compatibility.
This document was generated by Jens Thoms Toerring on September 6, 2017 using texi2html 1.82.