Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ✨ schedule module inspired by XAML #41

Merged
merged 5 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 57 additions & 35 deletions examples/WaveGenDemo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@

using Qynit.PulseGen;

using ScottPlot;

for (var i = 0; i < 5; i++)
{
RunDouble();
}

for (var i = 0; i < 5; i++)
{
RunSingle();
}
//for (var i = 0; i < 5; i++)
//{
// RunSingle();
//}
//RunDouble();

static void RunDouble()
{
Expand All @@ -35,29 +38,49 @@ static void Run<T>() where T : unmanaged, IFloatingPointIeee754<T>
var ch1 = phaseTrackingTransform.AddChannel(100e6);
var ch2 = phaseTrackingTransform.AddChannel(250e6);
var shape = new HannPulseShape();
phaseTrackingTransform.Play(ch1, new(shape, 30e-9, 100e-9), 0, 0, 0.5, 2e-9, 0);
phaseTrackingTransform.Play(ch2, new(shape, 30e-9, 100e-9), 0, 0, 0.6, 2e-9, 0);
phaseTrackingTransform.ShiftPhase(ch1, 0.25);
phaseTrackingTransform.ShiftPhase(ch2, -0.25);
phaseTrackingTransform.Play(ch1, new(shape, 30e-9, 100e-9), 0, 0, 0.5, 2e-9, 200e-9);
phaseTrackingTransform.Play(ch2, new(shape, 30e-9, 100e-9), 0, 0, 0.6, 2e-9, 200e-9);
phaseTrackingTransform.ShiftFrequency(ch1, -100e6, 400e-9);
phaseTrackingTransform.ShiftFrequency(ch2, -250e6, 400e-9);
phaseTrackingTransform.Play(ch1, new(shape, 200e-9, 0), 0, 0, 0.5, 2e-9, 400e-9);
phaseTrackingTransform.Play(ch2, new(shape, 200e-9, 0), 0, 0, 0.6, 2e-9, 400e-9);
phaseTrackingTransform.SetFrequency(ch1, 0, 600e-9);
phaseTrackingTransform.SetFrequency(ch2, 0, 600e-9);
var tStart = 600e-9;
var count = 0;
while (tStart < 49e-6)
{
phaseTrackingTransform.Play(ch1, new(shape, 30e-9, 0e-9), 0, 0, 0.5, 2e-9, tStart);
phaseTrackingTransform.Play(ch2, new(shape, 30e-9, 0e-9), 0, 0, 0.6, 2e-9, tStart);
phaseTrackingTransform.ShiftPhase(ch1, 0.25);
phaseTrackingTransform.ShiftPhase(ch2, -0.25);
tStart += 0.1e-9;
count++;
}
var stack = new StackSchedule();
stack.Add(new PlayElement(ch1, new(shape, 30e-9, 100e-9), 0, 0, 0.5, 2e-9));
stack.Add(new PlayElement(ch2, new(shape, 30e-9, 50e-9), 0, 0, 0.6, 2e-9));
stack.Add(new ShiftPhaseElement(ch1, 0.25));
stack.Add(new ShiftPhaseElement(ch2, -0.25));
stack.Add(new BarrierElement(ch1, ch2) { Margin = new(15e-9) });

var stack2 = new StackSchedule();
stack2.Add(new PlayElement(ch1, new(shape, 30e-9, 0), 0, 0, 0.5, 2e-9) { Margin = new(15e-9) });
stack2.Add(new PlayElement(ch1, new(shape, 30e-9, 0), 0, 0, 0.5, 2e-9) { Margin = new(15e-9) });
stack2.Add(new PlayElement(ch1, new(shape, 30e-9, 0), 0, 0, 0.5, 2e-9) { Margin = new(15e-9) });
stack.Add(stack2);

stack.Add(new BarrierElement(ch1, ch2) { Margin = new(15e-9) });

var grid = new GridSchedule() { Duration = 500e-9 };
grid.AddColumn(GridLength.Absolute(90e-9));
grid.AddColumn(GridLength.Star(1));
grid.AddColumn(GridLength.Absolute(90e-9));
grid.Add(new PlayElement(ch1, new(shape, 30e-9, 200e-9), 0, 0, 0.5, 2e-9) { Alignment = Qynit.PulseGen.Alignment.Center }, 1, 1);
grid.Add(new PlayElement(ch2, new(shape, 30e-9, 50e-9), -250e6, 0, 0.6, 2e-9) { Alignment = Qynit.PulseGen.Alignment.Stretch, FlexiblePlateau = true }, 0, 3);
stack.Add(grid);

stack.Add(new ShiftFrequencyElement(ch1, -100e6));
stack.Add(new ShiftFrequencyElement(ch2, -250e6));
stack.Add(new BarrierElement(ch1, ch2) { Margin = new(15e-9) });

var abs = new AbsoluteSchedule();
abs.Add(new PlayElement(ch1, new(null, 200e-9, 0), 0, 0, 0.5, 2e-9), 10e-9);
abs.Add(new RepeatElement(new PlayElement(ch2, new(null, 100e-9, 0), 0, 0, 0.6, 2e-9), 2) { Spacing = 10e-9 }, 210e-9);
stack.Add(abs);

stack.Add(new SetFrequencyElement(ch1, 0));
stack.Add(new SetFrequencyElement(ch2, 0));
stack.Add(new BarrierElement(ch1, ch2) { Margin = new(15e-9) });
stack.Add(new PlayElement(ch1, new(shape, 200e-9, 0), 0, 0, 0.5, 2e-9));
stack.Add(new PlayElement(ch2, new(shape, 100e-9, 0), 0, 0, 0.6, 2e-9));
var main = new GridSchedule();
main.Add(stack);
main.Measure(49.9e-6);
main.Arrange(0, 49.9e-6);
main.Render(0, phaseTrackingTransform);

var t1 = sw.Elapsed;

var pulseLists = phaseTrackingTransform.Finish();
Expand All @@ -73,14 +96,13 @@ static void Run<T>() where T : unmanaged, IFloatingPointIeee754<T>
Console.WriteLine($"Build time: {(t2 - t1).TotalMilliseconds} ms");
Console.WriteLine($"Sampling time: {(t3 - t2).TotalMilliseconds} ms");
Console.WriteLine($"Total elapsed time: {sw.Elapsed.TotalMilliseconds} ms");
Console.WriteLine($"Count = {count}");
using var waveform1 = waveforms[ch1];
using var waveform2 = waveforms[ch2];
//var plot = new Plot(1920, 1080);
//plot.AddSignal(waveform1.DataI[..2000].ToArray(), sampleRate, label: $"wave 1 real");
//plot.AddSignal(waveform1.DataQ[..2000].ToArray(), sampleRate, label: $"wave 1 imag");
//plot.AddSignal(waveform2.DataI[..2000].ToArray(), sampleRate, label: $"wave 2 real");
//plot.AddSignal(waveform2.DataQ[..2000].ToArray(), sampleRate, label: $"wave 2 imag");
//plot.Legend();
//plot.SaveFig("demo2.png");
var plot = new Plot(1920, 1080);
plot.AddSignal(waveform1.DataI[^4000..].ToArray(), sampleRate, label: $"wave 1 real");
plot.AddSignal(waveform1.DataQ[^4000..].ToArray(), sampleRate, label: $"wave 1 imag");
plot.AddSignal(waveform2.DataI[^4000..].ToArray(), sampleRate, label: $"wave 2 real");
plot.AddSignal(waveform2.DataQ[^4000..].ToArray(), sampleRate, label: $"wave 2 imag");
plot.Legend();
plot.SaveFig("demo2.png");
}
50 changes: 50 additions & 0 deletions src/Qynit.PulseGen/AbsoluteSchedule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Diagnostics;

using CommunityToolkit.Diagnostics;

namespace Qynit.PulseGen;
public class AbsoluteSchedule : Schedule
{
private readonly List<double> _elementTimes = new();
public void Add(ScheduleElement element)
{
Add(element, 0);
}

public void Add(ScheduleElement element, double time)
{
if (element.Parent is not null)
{
ThrowHelper.ThrowArgumentException("The element is already added to another schedule.");
}
if (!double.IsFinite(time))
{
ThrowHelper.ThrowArgumentException("The time is not finite.");
}
Children.Add(element);
element.Parent = this;
_elementTimes.Add(time);
}

protected override double ArrangeOverride(double time, double finalDuration)
{
foreach (var (element, elementTime) in Children.Zip(_elementTimes))
{
Debug.Assert(element.DesiredDuration is not null);
element.Arrange(elementTime, element.DesiredDuration.Value);
}
return finalDuration;
}

protected override double MeasureOverride(double maxDuration)
{
var maxTime = 0.0;
foreach (var (element, time) in Children.Zip(_elementTimes))
{
element.Measure(maxDuration);
Debug.Assert(element.DesiredDuration is not null);
maxTime = Math.Max(maxTime, time + element.DesiredDuration.Value);
}
return maxTime;
}
}
9 changes: 9 additions & 0 deletions src/Qynit.PulseGen/Alignment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Qynit.PulseGen;

public enum Alignment
{
End,
Start,
Center,
Stretch,
}
7 changes: 7 additions & 0 deletions src/Qynit.PulseGen/ArrangeOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Qynit.PulseGen;

public enum ArrangeOption
{
EndToStart,
StartToEnd,
}
24 changes: 24 additions & 0 deletions src/Qynit.PulseGen/BarrierElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Qynit.PulseGen;
public class BarrierElement : ScheduleElement
{
public override IReadOnlySet<int> Channels { get; }

public BarrierElement(params int[] channelIds) : this((IEnumerable<int>)channelIds) { }

public BarrierElement(IEnumerable<int> channelIds)
{
Channels = new HashSet<int>(channelIds);
}

protected override double ArrangeOverride(double time, double finalDuration)
{
return 0;
}

protected override double MeasureOverride(double maxDuration)
{
return 0;
}

protected override void RenderOverride(double time, PhaseTrackingTransform phaseTrackingTransform) { }
}
1 change: 1 addition & 0 deletions src/Qynit.PulseGen/Envelope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public readonly record struct Envelope
public IPulseShape? Shape { get; }
public double Width { get; }
public double Plateau { get; }
public double Duration => Plateau + Width;
public bool IsRectangle => Shape is null;
public bool IsZero => Width == 0 && Plateau == 0;
public Envelope(IPulseShape? shape, double width, double plateau)
Expand Down
24 changes: 24 additions & 0 deletions src/Qynit.PulseGen/GridLength.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using CommunityToolkit.Diagnostics;

namespace Qynit.PulseGen;
public readonly record struct GridLength(double Value, GridLengthUnit Unit)
{
public static GridLength Auto => new(double.NaN, GridLengthUnit.Auto);

public static GridLength Star(double value)
{
Guard.IsGreaterThan(value, 0);
return new(value, GridLengthUnit.Star);
}

public static GridLength Absolute(double value)
{
Guard.IsGreaterThanOrEqualTo(value, 0);
return new(value, GridLengthUnit.Second);
}

public bool IsAuto => Unit == GridLengthUnit.Auto;
public bool IsStar => Unit == GridLengthUnit.Star;
public bool IsAbsolute => Unit == GridLengthUnit.Second;
public bool IsValid => IsAuto || (IsStar && Value > 0) || (IsAbsolute && Value >= 0);
}
7 changes: 7 additions & 0 deletions src/Qynit.PulseGen/GridLengthUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Qynit.PulseGen;
public enum GridLengthUnit
{
Second,
Auto,
Star,
}
Loading