Skip to content
Snippets Groups Projects
Unverified Commit 1427f18a authored by Michiel Cottaar's avatar Michiel Cottaar
Browse files

Write MRIBuilder.Sequence to pulseq file

parent ff2ab433
No related branches found
No related tags found
1 merge request!1Add writing to Pulseq files
...@@ -3,14 +3,16 @@ Module converting MRIBuilder sequences to and from sequences recognised by [`Pul ...@@ -3,14 +3,16 @@ Module converting MRIBuilder sequences to and from sequences recognised by [`Pul
""" """
module Pulseq module Pulseq
import Interpolations: linear_interpolation import Interpolations: linear_interpolation
import ..PulseqIO.Types: PulseqSequence, PulseqBlock, PulseqTrapezoid, PulseqGradient import ..PulseqIO.Types: PulseqSequence, PulseqBlock, PulseqTrapezoid, PulseqGradient, PulseqRFPulse, PulseqShape, PulseqADC, PulseqExtension
import ...Containers: Sequence, BuildingBlock import ...Containers: Sequence, BuildingBlock, BaseBuildingBlock, events, waveform, iter_blocks
import ...Components: GenericPulse, ADC import ...Components: GenericPulse, ADC, RFPulseComponent
import ...Scanners: Scanner import ...Scanners: Scanner
import ...Variables: duration, nsamples, dwell_time, make_generic
function Sequence(pulseq::PulseqSequence; scanner=nothing, B0=3) function Sequence(pulseq::PulseqSequence; scanner=nothing, B0=nothing)
if isnothing(scanner) if isnothing(scanner)
scanner = Scanner(B0=B0) use_B0 = isnothing(B0) ? get(pulseq.definitions, :B0, 3.) : B0
scanner = Scanner(B0=use_B0)
end end
blocks = BuildingBlock.(pulseq.blocks; pulseq.definitions...) blocks = BuildingBlock.(pulseq.blocks; pulseq.definitions...)
return Sequence(blocks; name=Symbol(get(pulseq.definitions, :name, "from_pulseq")), scanner=scanner) return Sequence(blocks; name=Symbol(get(pulseq.definitions, :name, "from_pulseq")), scanner=scanner)
...@@ -88,4 +90,80 @@ function _get_amplitude(grad::PulseqGradient, time::Number, raster::Number) ...@@ -88,4 +90,80 @@ function _get_amplitude(grad::PulseqGradient, time::Number, raster::Number)
end end
function PulseqSequence(seq::Sequence{S}) where {S}
definitions = (
name=S,
AdcRasterTime=1e-9,
BlockDurationRaster=1e-9,
RadiofrequencyRasterTime=1e-9,
GradientRasterTime=1e-9,
TotalDuration=duration(seq) * 1e-3,
B0=seq.scanner.B0,
)
blocks = [PulseqBlock(block; definitions...) for (_, block) in iter_blocks(seq)]
return PulseqSequence(
v"1.4.0",
definitions,
blocks
)
end
function PulseqBlock(block::BaseBuildingBlock; BlockDurationRaster, AdcRasterTime, kwargs...)
rf = nothing
adc = nothing
for (delay, event) in events(block)
if event isa RFPulseComponent
if !isnothing(rf)
error("Pulseq does not support a single building block containing multiple RF pulses.")
end
gen = make_generic(event)
rf = PulseqRFPulse(
maximum(gen.amplitude) * 1e3,
PulseqShape(gen.amplitude ./ maximum(gen.amplitude)),
PulseqShape(deg2rad.(gen.phase)),
PulseqShape(gen.time .* 1e-3),
Int(div(delay, 1e-3)),
0.,
0.
)
elseif event isa ADC
if !isnothing(rf)
error("Pulseq does not support a single building block containing multiple ADC events.")
end
adc = PulseqADC(
nsamples(event),
div(dwell_time(event), AdcRasterTime),
Int(div(delay, 1e-3)),
0., 0.
)
else
error("Cannot write $(typeof(event)) to Pulseq.")
end
end
grads = []
times = [t for (t, _) in waveform(block)]
for dim in 1:3
amplitudes = [g[dim] for (_, g) in waveform(block)]
if iszero(maximum(abs.(amplitudes); init=0.))
push!(grads, nothing)
else
push!(grads, PulseqGradient(
maximum(amplitudes) * 1e3,
PulseqShape(amplitudes ./ maximum(amplitudes)),
PulseqShape(times .* 1e-3),
0.,
))
end
end
return PulseqBlock(
Int(div(1e-3 * duration(block), BlockDurationRaster)),
rf,
grads...,
adc,
Tuple{PulseqExtension, Int}[]
)
end
end end
\ No newline at end of file
...@@ -4,6 +4,7 @@ function parse_section(section:: PulseqSection{:definitions}; kwargs...) ...@@ -4,6 +4,7 @@ function parse_section(section:: PulseqSection{:definitions}; kwargs...)
end end
_to_string_value(value::AbstractString) = value _to_string_value(value::AbstractString) = value
_to_string_value(value::Symbol) = string(value)
_to_string_value(value::Number) = string(value) _to_string_value(value::Number) = string(value)
_to_string_value(value::Vector) = join(_to_string_value.(value), " ") _to_string_value(value::Vector) = join(_to_string_value.(value), " ")
......
...@@ -12,6 +12,7 @@ include("components.jl") ...@@ -12,6 +12,7 @@ include("components.jl")
include("parsers/parsers.jl") include("parsers/parsers.jl")
include("parse_sections.jl") include("parse_sections.jl")
import .Types: PulseqSequence
""" """
......
...@@ -4,7 +4,7 @@ include("pulseq_io/pulseq_io.jl") ...@@ -4,7 +4,7 @@ include("pulseq_io/pulseq_io.jl")
include("pulseq.jl") include("pulseq.jl")
import Serialization: serialize, deserialize import Serialization: serialize, deserialize
import .PulseqIO: read_pulseq import .PulseqIO: read_pulseq, write_pulseq, PulseqSequence
import ..Containers: Sequence import ..Containers: Sequence
...@@ -14,7 +14,7 @@ function read_sequence(filename::AbstractString, args...; kwargs...) ...@@ -14,7 +14,7 @@ function read_sequence(filename::AbstractString, args...; kwargs...)
end end
end end
function read_sequence(io::IO; format=nothing, kwargs...) function read_sequence(io::IO; format=nothing, B0=nothing, scanner=nothing,kwargs...)
if isnothing(format) if isnothing(format)
pos = position(io) pos = position(io)
for format in (:pulseq, :serialize) for format in (:pulseq, :serialize)
...@@ -27,7 +27,7 @@ function read_sequence(io::IO; format=nothing, kwargs...) ...@@ -27,7 +27,7 @@ function read_sequence(io::IO; format=nothing, kwargs...)
error("Could not read the input filename. Tried all formats (:pulseq/:serialize).") error("Could not read the input filename. Tried all formats (:pulseq/:serialize).")
end end
if format == :pulseq if format == :pulseq
return Sequence(read_pulseq(io, kwargs...)) return Sequence(read_pulseq(io, kwargs...), B0=B0, scanner=scanner)
elseif format == :serialize elseif format == :serialize
return deserialize(io, kwargs...) return deserialize(io, kwargs...)
else else
...@@ -42,8 +42,10 @@ function write_sequence(filename::AbstractString, args...; kwargs...) ...@@ -42,8 +42,10 @@ function write_sequence(filename::AbstractString, args...; kwargs...)
end end
end end
function write_sequence(io::IO, sequence; format::Symbol=:serialize) function write_sequence(io::IO, sequence; format::Symbol=:pulseq)
if format == :serialize if format == :pulseq
write_pulseq(io, PulseqSequence(sequence))
elseif format == :serialize
serialize(io, sequence) serialize(io, sequence)
else else
error("Unrecognised selected format for output ($format). Only :serialize is supported..") error("Unrecognised selected format for output ($format). Only :serialize is supported..")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment