Page tree

With the timing pattern, the timing system provides a flexible way of configuring bunch trains and auxiliary triggers. The timing pattern is originally defined in the (mostly outdated) specifications from http://ttfinfo2.desy.de/doocs/Timing/CDRv2.2short.pdf and an additional document. The timing pattern is basically an array of 7200 integers or timing words, attached to a 9-MHz clock (the first entry corresponds to time t0, the next one to t0+111 ns, and so on). Each of these words define what kinds of triggers or bunches are to be generated at that time. The details, like the bunch destination and charge, are encoded in the 32 bits of the timing word:

The timing word

A 32-bit timing word can be represented by the TimingWord class defined in timing_pattern.h. This class offers convenient access to the most important pieces of information stored in the word. See https://ttfinfo.desy.de/doocs/doocs_libs/DOOCSddaqlib/html/classdoocs_1_1TimingWord.html for details.

#include <timing_pattern.h>

doocs::TimingWord w = d_timing_pattern_.value(0); // Assume that d_timing_pattern_ is a reference to the timing pattern

// Return the charge range.
doocs::ChargeRange c = w.get_charge_range();

// Return the destination.
doocs::Destination d = w.get_destination();

// Return the injector laser bits (4..7).
uint8_t bits = get_injector_laser_bits();

Accessing the bunch pattern

C++ DOOCS servers with access to the DAQ shared memory can subscribe to the timing pattern via the the D_DAQtiming class from the DOOCSddaq library:

#include <D_DAQtiming.h>

// Declaration & definition
D_DAQtiming d_timing_pattern_{"TIMING_PATTERN Timing pattern", SPECTRUM_LENGTH, this};

// Initialization
void MyEqFct::init()
{
	d_timing_pattern_.init();
}

// On DAQ interrupt
void MyEqFct::interrupt_usr1(int sig_no)
{
    d_timing_pattern_.interrupt_usr1(sig_no);
	if (d_timing_pattern_.ok())
    {
        int macropulse_number = d_timing_pattern_->get_mpnum(); // get current macropulse number
        int current_buffer = macropulse_number & 0xf; // calculate buffer number from macropulse number (this may differ for other servers)
        for (int i = 0; i < d_timing_pattern_.length(); i++)
        {
            // Print the timing word...
            std::cout << i << ": " << d_timing_pattern_.value(i, current_buffer) << "\n";
            // ... or store it as a TimingWord that offers more functionality:
            doocs::TimingWord w = d_timing_pattern_.value(i, current_buffer);
        }
    }
}

Other servers can subscribe to the timing pattern via ZeroMQ:

#include <D_ZMQbase.h>

// Declaration & definition
doocs::D_ZMQintarray d_timing_pattern_("TIMING_PATTERN Timing pattern", SPECTRUM_LENGTH, this);

// Initialization
void MyEqFct::post_init()
{
    d_timing_pattern_->attach();
    // Note that other code is needed to set up the ZeroMQ subscription
}

// On ZeroMQ callback
void MyEqFct::interrupt_usr1(int)
{
    for (int i = 0; i < d_timing_pattern_.length(); i++)
    {
        // Print the timing word...
        std::cout << i << ": " << d_timing_pattern_.value(i) << "\n";
        // ... or store it as a TimingWord that offers more functionality:
        doocs::TimingWord w = d_timing_pattern_.value(i);
    } 
}

Iterating over the timing pattern

The two code examples above already show how to write a simple loop over the timing pattern. However, the timing pattern in isolation is seldomly useful and things become a little more complicated when other data from a D_spectrum are to be processed. This data

  • can be sampled with a frequency that is different from the 9 MHz of the timing pattern,
  • can have an arbitrary number of pre-samples, and of course
  • it can have a different length.

The function template scan_timing_pattern() from timing_pattern.h helps with such applications: It iterates over the timing pattern and a reference spectrum simultaneously, calling a user-defined function for each entry of the reference spectrum:

#include <timing_pattern.h>

D_float d_charge_threshold_( ... );
D_DAQtiming d_timing_pattern_( ... ); // Also works with D_intarray
D_DAQspectrum d_bunch_charge_( ... ); // Also works with D_spectrum

doocs::scan_timing_pattern(d_timing_pattern_, d_bunch_charge_,
    [=](int idx, doocs::TimingWord timing_word)
    {
        if (timing_word.get_destination() == 0)
            return;

        if (d_bunch_charge_.read_spectrum(idx) >= THRESHOLD)
        {
            std::cerr << "Index " << idx << ": ";
            if (doocs::xfel::is_subtrain_sa1(timing_word)) // test for XFEL subtrain SA1
                std::cerr << "subtrain SA1, ";
            auto charge_range = timing_word.get_charge_range();
            std::cerr << "max. charge " << charge_range.get_upper_limit() << " nC, ";
            std::cerr << "destination " << timing_word.get_destination() << "\n";
        }
    });

scan_timing_pattern() is called with a user-defined function that receives two input parameters, the index into the data spectrum and the corresponding timing word. See https://ttfinfo.desy.de/doocs/doocs_libs/DOOCSddaqlib/html/namespacedoocs.html#aad563907f6b6d9d532eecdf00ba3202a for more details.

Subtrain test functions (accelerator specific)

As shown in the previous example, a number of functions from timing_pattern.h allow testing if the bunch described by a certain TimingWord belongs to a subtrain of the machine:

// These functions are imported by #include <timing_pattern.h>

// Functions for FLASH
bool doocs::flash::is_subtrain_all(TimingWord w); // Does this timing word describe a bunch belonging to subtrain ALL?
bool doocs::flash::is_subtrain_flash1(TimingWord w); // Does this timing word describe a bunch belonging to subtrain FLASH1?
bool doocs::flash::is_subtrain_flash2(TimingWord w); // etc.
bool doocs::flash::is_subtrain_flash3(TimingWord w);
bool doocs::flash::is_subtrain_laser1(TimingWord w);
bool doocs::flash::is_subtrain_laser2(TimingWord w);
bool doocs::flash::is_subtrain_laser3(TimingWord w);

// Functions for the European XFEL
bool doocs::xfel::is_subtrain_all(TimingWord w); // Does this timing word describe a bunch belonging to subtrain ALL?
bool doocs::xfel::is_subtrain_dud(TimingWord w); /// Does this timing word describe a bunch belonging to subtrain DUD?
bool doocs::xfel::is_subtrain_sa1(TimingWord w);
bool doocs::xfel::is_subtrain_sa2(TimingWord w);
bool doocs::xfel::is_subtrain_sa3(TimingWord w);

For details, see https://ttfinfo.desy.de/doocs/doocs_libs/DOOCSddaqlib/html/timing__pattern_8h.html.

Machine-independent subtrain tests

The subtrain test functions listed above are specific to a certain accelerator. The HLC machine library allows listing all available subtrains for a machine and testing for them in an accelerator-independent manner:

#include <hlc_machine.h>

using namespace hlc::machine;

Machine machine(get_facility_name()); // Creates a FLASH/XFEL/VXFEL/SINBAD machine object according to the facility name.

auto ti = machine.get_timing_info(); // Get a TimingInfo object

for (int subtrain = 1; subtrain <= machine.get_num_subtrains(); subtrain++)
{
    SubtrainTestFctPtr is_subtrain = ti->get_subtrain_test_function(subtrain);

    doocs::TimingWord timing_word = ... // obtain timing info for some bunch from somewhere

    if (is_subtrain(timing_word))
        cout << "Bunch belongs to subtrain " << machine.get_subtrain_name(subtrain) << "\n";
}