Skip to content

Key Features

Design Philosophy

The interpreter is built around three core principles:

  • 🎯Accurate


    If it runs, it is exactly as you requested

    More details

  • 🧩Compatible


    Runs almost any Pulseq file, even if they are made for other vendors

    More details

  • ⚙Flexible


    Simple by default, while powerful & advanced if needed

    More details


Accuracy

If it runs, it is exactly as you requested

  • ❌Conversion
    The interpreter directly runs .seq files - no extra steps, no scripts, or files are required.
    Quickstart Guide

  • ❌Sequence detection
    There is no detection of currently common sequence types.
    If you're building something completely new, it will run - without updating the compiler.

Interpreter architecture

When running conventional full Pulseq sequences, a Philips "Spectroscopy" sequence is started. This sequence is treated as dummy sequence, as the interpreter modifies the sequence to match the Pulseq sequence block-by-block.
While Philips sequences are usually built with clever re-use of objects and sequence building blocks, this approach of running Pulseq offers the full flexibility to run any type of sequence!

Pulseq interpreter architecture

Overview of the interpreter architecture

Reconstruction & raw data

For conventional "full" (non-hybrid) sequences, that leverage Pulseq encoding, the interpreter currently does not yet support on-scanner Philips reconstruction.
You will need to reconstruct images from the raw data. When using hybrid sequences, the native reconstruction can also be used!


Compatibility

Runs almost any Pulseq file, even if they are made for other vendors

As Philips is one of the later vendors to gain Pulseq support, there are already lots of sequences available online.
Therefore, this interpreter is designed to run as much existing sequences as possible!

Hardware compatibility

While the interpreter and many sequences are free... Pulseq is not free a hardware specification upgrade!
Sequences designed for another scanner may be incompatible with yours. For example:

  • gradient specs (strength, slew rate)
  • max B1+
  • sound levels
  • PNS

and other aspects could be more or less limiting than other systems...

This compatibility is achieved through using the existing C++ reference interpreter from Pulseq itself - and extra optimizatons:

Blocks & delays

The Pulseq specification lists the duration of blocks as an integer of the BlockDurationRaster. Similarly, the delays of events are specified as \(\mu\)s, again in whole integers.
During testing with Philips native dwell times, we found the MATLAB library to generate .seq files with non-integer delays. As the Philips platform has no \(\mu\)s reqirement, the interpreter was modified to support floating-point delays and durations.

Block merging

MRI scanners commonly need time to switch the RF chain between transmit and receive states (tuning and detuning coils). RF pulses and ADC events therefore need extra time before and/or after them.

While the Pulseq libraries support designing sequences around these (using the so-called DeadTime and RingdownTime system options) - this interpreter prevents their need all together.
By automatically merging multiple Pulseq blocks into 1 large block, the requirement of extra time is removed:

Diffusion scan with EPI readout

Pulse sequence diagram of a diffusion scan with EPI readout

Block #54 - #57 (4) [@1 ms]

Shows that 4 blocks are merged, from #54 to #57. And the [@1ms] means that set of blocks started at 1ms (after the gray line), with some gradients overflowing, to create more time for the preceeding RF pulse.

Raster times

Gradients (GR)

The raster times for the three different types of gradients in Pulseq are handled as:

Gradient TypeDescriptorMapping
Trapezodial[TRAP]Native Trapezodial GR objects. All timings are supported, without requiring any grid-allignment
Extended Trapezodial[GRADIENTS] with time waveformSplit up across multiple native GR objects
Arbitrary[GRADIENTS] without time waveformNative sampled gradients - and resampled if needed

RF pulses (RF)

The interpreter is able to accomodate different raster times through either:

  1. Changing the hardware dwell time
  2. Using the native flexibility, and its support for interpolation (recommended)

Flexibility

Simple by default, while powerful & advanced if needed

How simple? See the Quickstart Guide

Extensions

On top of supporting conventional Pulseq sequences, the interpreters is already extended with two novel features.

Parallel Transmission (pTx)

On Ultra High Field (UHF) MRI systems (≥7T), scans may become inhomogenous due to the inherently higher \(f_0\) frequency.

Most UHF MRI scanners offer multiple transmit (Tx) channels.

Each channel has its own \(\mathcal{\tilde{B}}_1^{(+)}\) field, that can be used as one single.
Or they can be used independantly, to improve the total \(\mathcal{\tilde{B}}_1^{(+)}\) field.

Single Tx GRE animation

Animaton of eight 2D slices, each using one of the 8 Tx channels

By exploiting the flexibility of the Pulseq framework, we've extended it to "pTx-Pulseq" that offers:

  • Per-channel waveforms: Individual control of all transmit channels
  • Complex waveforms: Full magnitude and phase control over all samples

pTx waveform storage

pTx power monitoring

Animation of the real-time power monitoring during a pTx sequence

pTx-Pulseq enables full dynamic pTx functionality (and also static shimming), by repeating the waveform's timepoints. This allows the storage of multiple channels, without requiring an updated spec or breaking existing interpreters.

\(B_0\) direction conversion

The direction of the main magnetic \(B_0\) field (inwards vs outwards) alters the \(\mathcal{\tilde{B}}_1^{(+)}\) fields a coil produces. pTx pulses are portable across vendors, but might require the conversion outlined in our paper.

Siemens support

While the mainline interpreter currently does not utilize the additional Tx channels in a pTx-Pulseq sequence, Dario Bosch implemented pTx-Pulseq support for all Siemens UHF scanner platforms.

Hybrid mode

Besides running normal full (pTx-)Pulseq scans, the interpreter also offers a hybrid mode.

Hybrid sequence architecture

Hybrid mode merges a pTx-Pulseq kT-points excitation module into a native MP-RAGE sequence

This hybrid mode bridges the gap between native scans, and Pulseq sequences. This allows for:

  • Best of both worlds: Pulseq flexibility + Native features (encoding, parameter flexbility, etc)
  • Gradual migration: Replace parts of existing sequences incrementally
  • Custom modules: Add specialized prep, excitation, or inversion modules
  • Validation: Compare against known-good native implementations

Modularity

Code architecture

graph LR

    subgraph FUNCTIONS["Functionality"]
        MMU_CONTENT["<i>Runtime conversion</i><br/>Pulseq → Philips"]
        MGG_CONTENT["<i>ExternalSequence.cpp</i><br/>.seq reader"]
        MPU_HYBRID["<i>Static conversion</i><br/>Pulseq → Philips"]
        MPU_FULL["<i>Dynamic conversion</i><br/>Sequence prep"]
        MPU_DECISION{"Mode?"}
    end
    
    subgraph BINARIES["Compiled Components"]
        MMU(mmupulseq.cpp)
        MGG(mggpulseq.cpp)
        MPU(mpupulseq.cpp)
    end
    
    subgraph PATCH["Pulseq Patch"]
        HOSTPC[Host PC]
        SPEC[Spectrometer]
    end

    MPU_HYBRID -->|Hybrid| MPU_DECISION
    MPU_FULL -->|Full| MPU_DECISION

    MGG_CONTENT ---> MGG
    MMU_CONTENT ---> MMU
    MPU_DECISION ---> MPU

    MGG -.-> HOSTPC
    MGG -.-> SPEC
    MMU --> SPEC
    MPU --> HOSTPC
    
    classDef binary stroke-width:3px,stroke-dasharray: 0 0
    classDef function stroke-width:2px,stroke-dasharray: 5 5
    classDef platform stroke-width:4px,stroke-dasharray: 10 2
    classDef decision stroke-width:4px,stroke-dasharray: 15 5 5 5
    
    class MGG,MMU,MPU binary
    class MGG_CONTENT,MMU_CONTENT,MPU_HYBRID,MPU_FULL function
    class MPU_DECISION decision
    class HOSTPC,SPEC platform

The interpreter is built around a modular architecture, with three main components. Each component is a separate file, allowing for easy merging of new features - as well as integration into other software releases.

Configuration

There is only one parameter exposed in the examcard UI.
This parameter enables or disables the live k-space data viewer, during the scan.

During testing and development, seeing the acquired data in real-time can be useful. To reduce the ovehead, this parameter can easily disable it.

Monitoring parameter

The other configuration options are set by adding lines to the bstrace.dat file.
These are the most important ones:

Log verbosity levels

bstrace.dat content

 
PULSEQ log_level_normal *
PULSEQ pdf_log_level_normal *
 
 
PULSEQ log_level_warning *
PULSEQ pdf_log_level_warning *
 
 
PULSEQ log_level_error *
PULSEQ pdf_log_level_error *
 
 
PULSEQ log_level_debug_low *
PULSEQ pdf_log_level_debug_low *
 
 
PULSEQ log_level_debug_medium *
PULSEQ pdf_log_level_debug_medium *
 
 
PULSEQ log_level_debug_high *
PULSEQ pdf_log_level_debug_high *
 

Logging output

...
+Pulseq: MSG_LEVEL = -1 (-2 to 3)
+Pulseq: Found 22 .seq files in patch folder:
+Pulseq: Opening G:\patch\test_gre.seq
...
...
+Pulseq: WARNING: signature validation failed! stored signature: 0cd0794cd9cc2205b4ed14149ef2873b; calculated hash: c6b9267dbad650817f109df9fb5194c2
+Pulseq: RF interpolation will be used, as dwell times do not match. HW is set to 16, seq file has 10 [100ns]
...
...
+Pulseq: *** ERROR: Failed to read file G:\patch\test_gre.seq\shapes.seq
+Pulseq: *** ERROR Cannot load external sequence
...
+Pulseq:   Starting! Current QSPY_par_meas = 29
+Pulseq: Start of SQ is Pulseq block #144 (zero-indexed!)
+Pulseq:      SQ::base->dur = 0.2444 [msec]
+Pulseq: Processing block #144 (#145 in .seq)
+Pulseq:    SQ::base->dur + 4.61 [msec] => 4.8544 [msec]
+Pulseq:    RF::ex->enable = 0
+Pulseq:   AQ::base->enable = 0
+Pulseq:     Next GR is objectID #1198 - using 1 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 1.95
+Pulseq:     GR::p->slope1 = 0.19
+Pulseq:     GR::p->slope2 = 0.19
+Pulseq:     GR::p->dur = 2.33
+Pulseq:   GR::p->str = 21.9503
+Pulseq:   Renamed to 'Trap #6'
+Pulseq:     GR::p->ori = 0
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq:     Next GR is objectID #1199 - using 2 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 0.78
+Pulseq:     GR::p->slope1 = 0.11
+Pulseq:     GR::p->slope2 = 0.11
+Pulseq:     GR::p->dur = 1
+Pulseq:   GR::p->str = 10.3085
+Pulseq:   Renamed to 'Trap #64'
+Pulseq:     GR::p->ori = 1
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq:     Next GR is objectID #1200 - using 3 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 1.24
+Pulseq:     GR::p->slope1 = 0.19
+Pulseq:     GR::p->slope2 = 0.19
+Pulseq:     GR::p->dur = 1.62
+Pulseq:   GR::p->str = 21.8992
+Pulseq:   Renamed to 'Trap #8'
+Pulseq:     GR::p->ori = 2
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq: Processing block #145 (#146 in .seq)
+Pulseq:    SQ::base->dur + 4.19 [msec] => 9.0444 [msec]
+Pulseq:   RF::ex_enable = 1
+Pulseq:   Renamed to 'RF #22 [u]'
+Pulseq:      RF::ex->phase_step3 = 135
+Pulseq:     Next GR is objectID #1201 - using 4 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 3
+Pulseq:     GR::p->slope1 = 0.09
+Pulseq:     GR::p->slope2 = 0.09
+Pulseq:     GR::p->dur = 3.18
+Pulseq:   GR::p->str = 10.4386
+Pulseq:   Renamed to 'Trap #1'
+Pulseq:     GR::p->ori = 2
+Pulseq:   GR::p->time = 5.62
+Pulseq:     Generated GR object!
+Pulseq: Processing block #146 (#147 in .seq)
+Pulseq:    SQ::base->dur + 1 [msec] => 10.0444 [msec]
+Pulseq:     Next GR is objectID #1202 - using 5 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 0.76
+Pulseq:     GR::p->slope1 = 0.12
+Pulseq:     GR::p->slope2 = 0.12
+Pulseq:     GR::p->dur = 1
+Pulseq:   GR::p->str = -13.6367
+Pulseq:   Renamed to 'Trap #2'
+Pulseq:     GR::p->ori = 0
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq:     Next GR is objectID #1203 - using 6 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 0.78
+Pulseq:     GR::p->slope1 = 0.11
+Pulseq:     GR::p->slope2 = 0.11
+Pulseq:     GR::p->dur = 1
+Pulseq:   GR::p->str = -10.2054
+Pulseq:   Renamed to 'Trap #65'
+Pulseq:     GR::p->ori = 1
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq:     Next GR is objectID #1204 - using 7 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 0.66
+Pulseq:     GR::p->slope1 = 0.17
+Pulseq:     GR::p->slope2 = 0.17
+Pulseq:     GR::p->dur = 1
+Pulseq:   GR::p->str = -19.4309
+Pulseq:   Renamed to 'Trap #4'
+Pulseq:     GR::p->ori = 2
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq: Processing block #147 (#148 in .seq)
+Pulseq:    SQ::base->dur + 1.63 [msec] => 11.6744 [msec]
+Pulseq: Processing block #148 (#149 in .seq)
+Pulseq:    SQ::base->dur + 3.8 [msec] => 15.4744 [msec]
+Pulseq:   Renamed to 'ADC #22'
+Pulseq:   AQ::base->enable = 1
+Pulseq:      AQ::base->measurement[0] WAS 29
+Pulseq:      AQ::base->comp_elements = 1
+Pulseq:      Generated AQ object!
+Pulseq:      AQ::base->row_nr = 0
+Pulseq:      AQ::base->start = 0.2467
+Pulseq:      AQ::base->stop = 3.2556
+Pulseq:       AQ::base->comp_y_prof = 0
+Pulseq:       AQ::base->comp_test_prof = 0
+Pulseq:      AQ::base->phase_step5 =~ 1610609643 - should be 135 [deg]
+Pulseq:     Next GR is objectID #1205 - using 8 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 3.2
+Pulseq:     GR::p->slope1 = 0.07
+Pulseq:     GR::p->slope2 = 0.07
+Pulseq:     GR::p->dur = 3.34
+Pulseq:   GR::p->str = 7.33964
+Pulseq:   Renamed to 'Trap #5'
+Pulseq:     GR::p->ori = 0
+Pulseq:   GR::p->time = 11.66
+Pulseq:     Generated GR object!
+Pulseq: Processing block #149 (#150 in .seq)
+Pulseq:   [Block contains no ADC anymore, not merging!]
+Pulseq:      'Block #145 - #149 (5) [@0.2444 ms]' is too long, cropping to 32 chars
+Pulseq:   Renamed to 'Block #145 - #149 (5) [@0.2444 m'
+Pulseq:     Sorted SQ objects by start time
+Pulseq:     Sorted of SQ objects:
+Pulseq:       0 [ms] -> SY::trig_scope
+Pulseq:       0.2444 [ms] -> GR::pulseq_0_
+Pulseq:       0.2444 [ms] -> GR::pulseq_1_
+Pulseq:       0.2444 [ms] -> GR::pulseq_2_
+Pulseq:       5.5131 [ms] -> RF::ex
+Pulseq:       5.8644 [ms] -> GR::pulseq_3_
+Pulseq:       9.0444 [ms] -> GR::pulseq_4_
+Pulseq:       9.0444 [ms] -> GR::pulseq_5_
+Pulseq:       9.0444 [ms] -> GR::pulseq_6_
+Pulseq:       11.7277 [ms] -> AQ::ADC #22
+Pulseq:       11.9044 [ms] -> GR::pulseq_7_
+Pulseq:   Computing minimum duration of this SQ block...
+Pulseq:       RF::ex->end = 8.7342 [ms]
+Pulseq:       AQ::base->end = 14.9856 [ms]
+Pulseq:       GR::p[0]->slope1 @ 0.19 [ms]
+Pulseq:       GR::p[1]->slope1 @ 0.11 [ms]
+Pulseq:       GR::p[2]->slope1 @ 0.19 [ms]
+Pulseq:       GR::p[3]->slope1 @ 5.71 [ms]
+Pulseq:       GR::p[4]->slope1 @ 8.92 [ms]
+Pulseq:       GR::p[5]->slope1 @ 8.91 [ms]
+Pulseq:       GR::p[6]->slope1 @ 8.97 [ms]
+Pulseq:       GR::p[7]->slope1 @ 11.73 [ms]
+Pulseq:       SY::trig_scope->end = 0.017 [ms]
+Pulseq: Found minimum duration of this SQ block: 14.9856 [ms]
+Pulseq:     SQ::base->dur = 15.23 [ms] - reduced with 0.2444 [ms]
+Pulseq: Attempting SQ::base->perform()...
+Pulseq: Found GVE waveform for RFVAR::ptx_ex -> id = 768
+Pulseq: Found GVE waveform for RFVAR::ptx_ex -> id = 768
+Pulseq:   SQ::base->perform() done!
+Pulseq:   Number of blocks:    5
+Pulseq:   Number of RF: 1
+Pulseq:   Number of ADC:  1
+Pulseq:   Number of GX: 3
+Pulseq:   Number of GY: 2
+Pulseq:   Number of GZ: 3
+Pulseq:   Starting! Current QSPY_par_meas = 30
+Pulseq: Start of SQ is Pulseq block #149 (zero-indexed!)
+Pulseq:      SQ::base->dur = 0.2444 [msec]
+Pulseq: Processing block #149 (#150 in .seq)
+Pulseq:    SQ::base->dur + 4.61 [msec] => 4.8544 [msec]
+Pulseq:    RF::ex->enable = 0
+Pulseq:   AQ::base->enable = 0
+Pulseq:     Next GR is objectID #1198 - using 1 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 1.95
+Pulseq:     GR::p->slope1 = 0.19
+Pulseq:     GR::p->slope2 = 0.19
+Pulseq:     GR::p->dur = 2.33
+Pulseq:   GR::p->str = 21.9503
+Pulseq:   Renamed to 'Trap #6'
+Pulseq:     GR::p->ori = 0
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq:     Next GR is objectID #1199 - using 2 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 0.78
+Pulseq:     GR::p->slope1 = 0.11
+Pulseq:     GR::p->slope2 = 0.11
+Pulseq:     GR::p->dur = 1
+Pulseq:   GR::p->str = 10.2054
+Pulseq:   Renamed to 'Trap #66'
+Pulseq:     GR::p->ori = 1
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq:     Next GR is objectID #1200 - using 3 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 1.24
+Pulseq:     GR::p->slope1 = 0.19
+Pulseq:     GR::p->slope2 = 0.19
+Pulseq:     GR::p->dur = 1.62
+Pulseq:   GR::p->str = 21.8992
+Pulseq:   Renamed to 'Trap #8'
+Pulseq:     GR::p->ori = 2
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq: Processing block #150 (#151 in .seq)
+Pulseq:    SQ::base->dur + 4.19 [msec] => 9.0444 [msec]
+Pulseq:   RF::ex_enable = 1
+Pulseq:   Renamed to 'RF #14 [u]'
+Pulseq:      RF::ex->phase_step3 = 45
+Pulseq:     Next GR is objectID #1201 - using 4 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 3
+Pulseq:     GR::p->slope1 = 0.09
+Pulseq:     GR::p->slope2 = 0.09
+Pulseq:     GR::p->dur = 3.18
+Pulseq:   GR::p->str = 10.4386
+Pulseq:   Renamed to 'Trap #1'
+Pulseq:     GR::p->ori = 2
+Pulseq:   GR::p->time = 5.62
+Pulseq:     Generated GR object!
+Pulseq: Processing block #151 (#152 in .seq)
+Pulseq:    SQ::base->dur + 1 [msec] => 10.0444 [msec]
+Pulseq:     Next GR is objectID #1202 - using 5 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 0.76
+Pulseq:     GR::p->slope1 = 0.12
+Pulseq:     GR::p->slope2 = 0.12
+Pulseq:     GR::p->dur = 1
+Pulseq:   GR::p->str = -13.6367
+Pulseq:   Renamed to 'Trap #2'
+Pulseq:     GR::p->ori = 0
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq:     Next GR is objectID #1203 - using 6 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 0.78
+Pulseq:     GR::p->slope1 = 0.11
+Pulseq:     GR::p->slope2 = 0.11
+Pulseq:     GR::p->dur = 1
+Pulseq:   GR::p->str = -10.1023
+Pulseq:   Renamed to 'Trap #67'
+Pulseq:     GR::p->ori = 1
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq:     Next GR is objectID #1204 - using 7 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 0.66
+Pulseq:     GR::p->slope1 = 0.17
+Pulseq:     GR::p->slope2 = 0.17
+Pulseq:     GR::p->dur = 1
+Pulseq:   GR::p->str = -19.4309
+Pulseq:   Renamed to 'Trap #4'
+Pulseq:     GR::p->ori = 2
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq: Processing block #152 (#153 in .seq)
+Pulseq:    SQ::base->dur + 1.63 [msec] => 11.6744 [msec]
+Pulseq: Processing block #153 (#154 in .seq)
+Pulseq:    SQ::base->dur + 3.8 [msec] => 15.4744 [msec]
+Pulseq:   Renamed to 'ADC #14'
+Pulseq:   AQ::base->enable = 1
+Pulseq:      AQ::base->measurement[0] WAS 30
+Pulseq:      AQ::base->comp_elements = 1
+Pulseq:      Generated AQ object!
+Pulseq:      AQ::base->row_nr = 0
+Pulseq:      AQ::base->start = 0.2467
+Pulseq:      AQ::base->stop = 3.2556
+Pulseq:       AQ::base->comp_y_prof = 0
+Pulseq:       AQ::base->comp_test_prof = 0
+Pulseq:      AQ::base->phase_step5 =~ 536870804 - should be 45 [deg]
+Pulseq:     Next GR is objectID #1205 - using 8 GR objects
+Pulseq:     GR::p->wf_str = 0
+Pulseq:     GR::p->lenc = 3.2
+Pulseq:     GR::p->slope1 = 0.07
+Pulseq:     GR::p->slope2 = 0.07
+Pulseq:     GR::p->dur = 3.34
+Pulseq:   GR::p->str = 7.33964
+Pulseq:   Renamed to 'Trap #5'
+Pulseq:     GR::p->ori = 0
+Pulseq:   GR::p->time = 11.66
+Pulseq:     Generated GR object!
+Pulseq: Processing block #154 (#155 in .seq)
+Pulseq:   [Block contains no ADC anymore, not merging!]
+Pulseq:      'Block #150 - #154 (5) [@0.2444 ms]' is too long, cropping to 32 chars
+Pulseq:   Renamed to 'Block #150 - #154 (5) [@0.2444 m'
+Pulseq:     Sorted SQ objects by start time
+Pulseq:     Sorted of SQ objects:
+Pulseq:       0 [ms] -> SY::trig_scope
+Pulseq:       0.2444 [ms] -> GR::pulseq_0_
+Pulseq:       0.2444 [ms] -> GR::pulseq_1_
+Pulseq:       0.2444 [ms] -> GR::pulseq_2_
+Pulseq:       5.5131 [ms] -> RF::ex
+Pulseq:       5.8644 [ms] -> GR::pulseq_3_
+Pulseq:       9.0444 [ms] -> GR::pulseq_4_
+Pulseq:       9.0444 [ms] -> GR::pulseq_5_
+Pulseq:       9.0444 [ms] -> GR::pulseq_6_
+Pulseq:       11.7277 [ms] -> AQ::ADC #14
+Pulseq:       11.9044 [ms] -> GR::pulseq_7_
+Pulseq:   Computing minimum duration of this SQ block...
+Pulseq:       RF::ex->end = 8.7342 [ms]
+Pulseq:       AQ::base->end = 14.9856 [ms]
+Pulseq:       GR::p[0]->slope1 @ 0.19 [ms]
+Pulseq:       GR::p[1]->slope1 @ 0.11 [ms]
+Pulseq:       GR::p[2]->slope1 @ 0.19 [ms]
+Pulseq:       GR::p[3]->slope1 @ 5.71 [ms]
+Pulseq:       GR::p[4]->slope1 @ 8.92 [ms]
+Pulseq:       GR::p[5]->slope1 @ 8.91 [ms]
+Pulseq:       GR::p[6]->slope1 @ 8.97 [ms]
+Pulseq:       GR::p[7]->slope1 @ 11.73 [ms]
+Pulseq:       SY::trig_scope->end = 0.017 [ms]
+Pulseq: Found minimum duration of this SQ block: 14.9856 [ms]
+Pulseq:     SQ::base->dur = 15.23 [ms] - reduced with 0.2444 [ms]
+Pulseq: Attempting SQ::base->perform()...
+Pulseq: Found GVE waveform for RFVAR::ptx_ex -> id = 768
+Pulseq: Found GVE waveform for RFVAR::ptx_ex -> id = 768
+Pulseq:   SQ::base->perform() done!
+Pulseq:   Number of blocks:    5
+Pulseq:   Number of RF: 1
+Pulseq:   Number of ADC:  1
+Pulseq:   Number of GX: 3
+Pulseq:   Number of GY: 2
+Pulseq:   Number of GZ: 3
+Pulseq:   Number of TRIG: 0
+Pulseq:   Starting! Current QSPY_par_meas = 31
+Pulseq: Start of SQ is Pulseq block #154 (zero-indexed!)
+Pulseq: Processing block #154 (#155 in .seq)
+Pulseq:    SQ::base->dur + 4.61 [msec] => 4.8544 [msec]
+Pulseq:    RF::ex->enable = 0
+Pulseq:   AQ::base->enable = 0
+Pulseq:   GR::p->str = 21.9503
+Pulseq:   Renamed to 'Trap #6'
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq:   GR::p->str = 10.1023
+Pulseq:   Renamed to 'Trap #68'
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq:   GR::p->str = 21.8992
+Pulseq:   Renamed to 'Trap #8'
+Pulseq:   GR::p->time = 0
+Pulseq:     Generated GR object!
+Pulseq: Processing block #155 (#156 in .seq)
+Pulseq:    SQ::base->dur + 4.19 [msec] => 9.0444 [msec]
+Pulseq:   RF::ex_enable = 1
+Pulseq:   Renamed to 'RF #15 [u]'
+Pulseq:   GR::p->str = 10.4386
+Pulseq:   Renamed to 'Trap #1'
+Pulseq:   GR::p->time = 5.62
+Pulseq:     Generated GR object!
+Pulseq: Processing block #156 (#157 in .seq)
+Pulseq:    SQ::base->dur + 1 [msec] => 10.0444 [msec]
+Pulseq:   GR::p->str = -13.6367
+Pulseq:   Renamed to 'Trap #2'
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq:   GR::p->str = -9.99922
+Pulseq:   Renamed to 'Trap #69'
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq:   GR::p->str = -19.4309
+Pulseq:   Renamed to 'Trap #4'
+Pulseq:   GR::p->time = 8.8
+Pulseq:     Generated GR object!
+Pulseq: Processing block #157 (#158 in .seq)
+Pulseq:    SQ::base->dur + 1.63 [msec] => 11.6744 [msec]
+Pulseq: Processing block #158 (#159 in .seq)
+Pulseq:    SQ::base->dur + 3.8 [msec] => 15.4744 [msec]
+Pulseq:   Renamed to 'ADC #15'
+Pulseq:   AQ::base->enable = 1
+Pulseq:      Generated AQ object!
+Pulseq:   GR::p->str = 7.33964
+Pulseq:   Renamed to 'Trap #5'
+Pulseq:   GR::p->time = 11.66
+Pulseq:     Generated GR object!
+Pulseq: Processing block #159 (#160 in .seq)
+Pulseq:   [Block contains no ADC anymore, not merging!]
+Pulseq:   Renamed to 'Block #155 - #159 (5) [@0.2444 m'
+Pulseq:   Computing minimum duration of this SQ block...
+Pulseq: Found minimum duration of this SQ block: 14.9856 [ms]
+Pulseq: Attempting SQ::base->perform()...
+Pulseq:   SQ::base->perform() done!
+Pulseq:   Number of blocks:    5
+Pulseq:   Number of RF: 1
+Pulseq:   Number of ADC:  1
+Pulseq:   Number of GX: 3
+Pulseq:   Number of GY: 2
+Pulseq:   Number of GZ: 3
+Pulseq:  NSA count is 0, so restart -> pulseq_cur_block = 0
+Pulseq: Start of SQ is Pulseq block #0 (zero-indexed!)
+Pulseq: Processing block #0 (#1 in .seq)
+Pulseq: Processing block #1 (#2 in .seq)
+Pulseq: Processing block #2 (#3 in .seq)
+Pulseq: Processing block #3 (#4 in .seq)
+Pulseq: Processing block #4 (#5 in .seq)
+Pulseq: Found minimum duration of this SQ block: 10.3756 [ms]
+Pulseq: Attempting SQ::base->perform()...
+Pulseq: Start of SQ is Pulseq block #4 (zero-indexed!)
+Pulseq: Processing block #4 (#5 in .seq)
+Pulseq: Processing block #5 (#6 in .seq)
+Pulseq: Processing block #6 (#7 in .seq)
+Pulseq: GR::p->enable = 1
+Pulseq: GR::p->enable = 1
+Pulseq: Processing block #7 (#8 in .seq)
+Pulseq: Processing block #8 (#9 in .seq)
+Pulseq: GR::p->enable = 1
+Pulseq: Processing block #9 (#10 in .seq)
+Pulseq: Found minimum duration of this SQ block: 14.9856 [ms]
+Pulseq: Attempting SQ::base->perform()...
Amount of logging

The lower log levels are very verbose. The amount of data can quickly become GBs per scan!
While logging has no performance impact, messages might be skipped if needed.

Checksum Validation

By default, the interpreter will not run a .seq file if the checksum validation fails. This ensures that is not corrupted or modified in an unexpected way.

During testing or development, it can be useful to run (manually) edited sequences. To not restrict this advanced usage, you can convert the error into a warning:

bstrace.dat content

 

PULSEQ allow_unsigned *
 
# Line deactivated using '#'
#PULSEQ allow_unsigned *
 

Examcard UI

Warning due to checksum

Conflict exam due to checksum

Be careful with invalid checksums

The checksum validation is vital to ensure the integrity of the sequence. Normal usage should never trigger this.
We strongly recommend to keep it enabled. Especially on real scanner hardware!


Getting Started

Ready to try these features? Check out our quickstart guide or explore the examples to see them in action.