-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ separate array storage and timing info (#8)
* feat: pooled complex array storage * feat: ✨ switch to `PooledComplexArray` and use generic math * feat: simplify `PooledComplexArray` Slicing
- Loading branch information
Showing
23 changed files
with
907 additions
and
529 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,106 @@ | ||
using System.Diagnostics; | ||
using System.Numerics; | ||
|
||
using Qynit.Pulsewave; | ||
|
||
using ScottPlot; | ||
|
||
var sw = Stopwatch.StartNew(); | ||
|
||
var ch1 = new Channel("ch1"); | ||
var ch2 = new Channel("ch2"); | ||
|
||
var inputNode1 = new InputNode(); | ||
var inputNode2 = new InputNode(); | ||
|
||
var n = 100000; | ||
var sampleRate = 2e9; | ||
var outputNode1 = new OutputNode(n, sampleRate, 0); | ||
var outputNode2 = new OutputNode(n, sampleRate, 0); | ||
|
||
ConnectNode(inputNode1, outputNode1); | ||
ConnectNode(inputNode2, outputNode2); | ||
|
||
var generator = new WaveformGenerator(); | ||
generator.AddChannel(ch1, inputNode1, 100e6); | ||
generator.AddChannel(ch2, inputNode2, 250e6); | ||
|
||
var instructions = new List<Instruction>(); | ||
var shape = new HannPulseShape(); | ||
instructions.Add(new Play(shape, 0, 30e-9, 100e-9, 0.5, 0, 0, ch1)); | ||
instructions.Add(new Play(shape, 0, 30e-9, 100e-9, 0.6, 0, 0, ch2)); | ||
instructions.Add(new ShiftPhase(0.25 * Math.Tau, ch1)); | ||
instructions.Add(new ShiftPhase(-0.25 * Math.Tau, ch2)); | ||
instructions.Add(new Play(shape, 200e-9, 30e-9, 100e-9, 0.5, 0, 0, ch1)); | ||
instructions.Add(new Play(shape, 200e-9, 30e-9, 100e-9, 0.6, 0, 0, ch2)); | ||
instructions.Add(new ShiftFrequency(-100e6, 400e-9, ch1)); | ||
instructions.Add(new ShiftFrequency(-250e6, 400e-9, ch2)); | ||
instructions.Add(new Play(shape, 400e-9, 30e-9, 100e-9, 0.5, 0, 0, ch1)); | ||
instructions.Add(new Play(shape, 400e-9, 30e-9, 100e-9, 0.6, 0, 0, ch2)); | ||
instructions.Add(new SetFrequency(0, 600e-9, ch1)); | ||
instructions.Add(new SetFrequency(0, 600e-9, ch2)); | ||
|
||
var tStart = 600e-9; | ||
var count = 0; | ||
while (tStart < 49e-6) | ||
for (var i = 0; i < 5; i++) | ||
{ | ||
instructions.Add(new Play(shape, tStart, 30e-9, 0, 0.5, 0, 0, ch1)); | ||
instructions.Add(new Play(shape, tStart, 30e-9, 0, 0.6, 0, 0, ch2)); | ||
instructions.Add(new ShiftPhase(0.25 * Math.Tau, ch1)); | ||
instructions.Add(new ShiftPhase(-0.25 * Math.Tau, ch2)); | ||
tStart += 0.1e-9; | ||
count++; | ||
RunDouble(); | ||
} | ||
|
||
generator.Run(instructions); | ||
for (var i = 0; i < 5; i++) | ||
{ | ||
RunSingle(); | ||
} | ||
|
||
sw.Stop(); | ||
Console.WriteLine($"Elapsed time: {sw.Elapsed.TotalMilliseconds} ms"); | ||
Console.WriteLine($"Count = {count}"); | ||
static void ConnectNode<T>(IFilterNode<T> source, IFilterNode<T> target) where T : unmanaged, IFloatingPointIeee754<T> | ||
{ | ||
source.Outputs.Add(target); | ||
target.Inputs.Add(source); | ||
} | ||
|
||
using var waveform1 = outputNode1.TakeWaveform(); | ||
using var waveform2 = outputNode2.TakeWaveform(); | ||
static void RunDouble() | ||
{ | ||
Console.WriteLine("RunDouble:"); | ||
Run<double>(); | ||
Console.WriteLine("------------------------"); | ||
} | ||
|
||
var plot = new Plot(); | ||
plot.AddSignal(waveform1.DataI.ToArray(), sampleRate, label: $"wave 1 real"); | ||
plot.AddSignal(waveform1.DataQ.ToArray(), sampleRate, label: $"wave 1 imag"); | ||
plot.AddSignal(waveform2.DataI.ToArray(), sampleRate, label: $"wave 2 real"); | ||
plot.AddSignal(waveform2.DataQ.ToArray(), sampleRate, label: $"wave 2 imag"); | ||
plot.Legend(); | ||
plot.SaveFig("demo.png"); | ||
static void RunSingle() | ||
{ | ||
Console.WriteLine("RunSingle:"); | ||
Run<float>(); | ||
Console.WriteLine("------------------------"); | ||
} | ||
|
||
static void ConnectNode(IFilterNode source, IFilterNode target) | ||
static void Run<T>() where T : unmanaged, IFloatingPointIeee754<T> | ||
{ | ||
source.Outputs.Add(target); | ||
target.Inputs.Add(source); | ||
var sw = Stopwatch.StartNew(); | ||
|
||
var ch1 = new Channel("ch1"); | ||
var ch2 = new Channel("ch2"); | ||
|
||
var inputNode1 = new InputNode<T>(); | ||
var inputNode2 = new InputNode<T>(); | ||
|
||
var n = 100000; | ||
var sampleRate = 2e9; | ||
var outputNode1 = new OutputNode<T>(n, sampleRate, 0, -4); | ||
var outputNode2 = new OutputNode<T>(n, sampleRate, 0, -4); | ||
|
||
ConnectNode(inputNode1, outputNode1); | ||
ConnectNode(inputNode2, outputNode2); | ||
|
||
var generator = new WaveformGenerator<T>(); | ||
generator.AddChannel(ch1, inputNode1, 100e6); | ||
generator.AddChannel(ch2, inputNode2, 250e6); | ||
|
||
var instructions = new List<Instruction>(); | ||
var shape = new HannPulseShape(); | ||
instructions.Add(new Play(shape, 0, 30e-9, 100e-9, 0.5, 0, 0, ch1)); | ||
instructions.Add(new Play(shape, 0, 30e-9, 100e-9, 0.6, 0, 0, ch2)); | ||
instructions.Add(new ShiftPhase(0.25 * Math.Tau, ch1)); | ||
instructions.Add(new ShiftPhase(-0.25 * Math.Tau, ch2)); | ||
instructions.Add(new Play(shape, 200e-9, 30e-9, 100e-9, 0.5, 0, 0, ch1)); | ||
instructions.Add(new Play(shape, 200e-9, 30e-9, 100e-9, 0.6, 0, 0, ch2)); | ||
instructions.Add(new ShiftFrequency(-100e6, 400e-9, ch1)); | ||
instructions.Add(new ShiftFrequency(-250e6, 400e-9, ch2)); | ||
instructions.Add(new Play(shape, 400e-9, 30e-9, 100e-9, 0.5, 0, 0, ch1)); | ||
instructions.Add(new Play(shape, 400e-9, 30e-9, 100e-9, 0.6, 0, 0, ch2)); | ||
instructions.Add(new SetFrequency(0, 600e-9, ch1)); | ||
instructions.Add(new SetFrequency(0, 600e-9, ch2)); | ||
|
||
var tStart = 600e-9; | ||
var count = 0; | ||
while (tStart < 49e-6) | ||
{ | ||
//instructions.Add(new Play(shape, tStart, 30e-9, 0, 0.5, 0, 0, ch1)); | ||
//instructions.Add(new Play(shape, tStart, 30e-9, 0, 0.6, 0, 0, ch2)); | ||
instructions.Add(new Play(shape, tStart, 30e-9, 500e-9, 0.5, 0, 0, ch1)); | ||
instructions.Add(new Play(shape, tStart, 30e-9, 500e-9, 0.6, 0, 0, ch2)); | ||
instructions.Add(new ShiftPhase(0.25 * Math.Tau, ch1)); | ||
instructions.Add(new ShiftPhase(-0.25 * Math.Tau, ch2)); | ||
tStart += 0.1e-9; | ||
count++; | ||
} | ||
|
||
var t1 = sw.Elapsed; | ||
|
||
generator.Run(instructions); | ||
|
||
sw.Stop(); | ||
var t2 = sw.Elapsed; | ||
Console.WriteLine($"Instructions time: {t1.TotalMilliseconds} ms"); | ||
Console.WriteLine($"Run time: {(t2 - t1).TotalMilliseconds} ms"); | ||
Console.WriteLine($"Total elapsed time: {sw.Elapsed.TotalMilliseconds} ms"); | ||
Console.WriteLine($"Count = {count}"); | ||
using var waveform1 = outputNode1.TakeWaveform(); | ||
using var waveform2 = outputNode2.TakeWaveform(); | ||
//var plot = new Plot(1920, 1080); | ||
//plot.AddSignal(waveform1.DataI[..300].ToArray(), sampleRate, label: $"wave 1 real"); | ||
//plot.AddSignal(waveform1.DataQ[..300].ToArray(), sampleRate, label: $"wave 1 imag"); | ||
//plot.AddSignal(waveform2.DataI[..300].ToArray(), sampleRate, label: $"wave 2 real"); | ||
//plot.AddSignal(waveform2.DataQ[..300].ToArray(), sampleRate, label: $"wave 2 imag"); | ||
//plot.Legend(); | ||
//plot.SaveFig("demo2.png"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System.Diagnostics; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Qynit.Pulsewave; | ||
public readonly ref struct ComplexArrayReadOnlySpan<T> | ||
where T : unmanaged | ||
{ | ||
public ReadOnlySpan<T> DataI { get; } | ||
public ReadOnlySpan<T> DataQ { get; } | ||
public int Length => DataI.Length; | ||
internal ComplexArrayReadOnlySpan(ReadOnlySpan<T> dataI, ReadOnlySpan<T> dataQ) | ||
{ | ||
Debug.Assert(dataI.Length == dataQ.Length); | ||
DataI = dataI; | ||
DataQ = dataQ; | ||
} | ||
public static implicit operator ComplexArrayReadOnlySpan<T>(PooledComplexArray<T> source) | ||
{ | ||
return new ComplexArrayReadOnlySpan<T>(source.DataI, source.DataQ); | ||
} | ||
public static implicit operator ComplexArrayReadOnlySpan<T>(ComplexArraySpan<T> source) | ||
{ | ||
return new ComplexArrayReadOnlySpan<T>(source.DataI, source.DataQ); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public ComplexArrayReadOnlySpan<T> Slice(int start) | ||
{ | ||
return new ComplexArrayReadOnlySpan<T>(DataI[start..], DataQ[start..]); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public ComplexArrayReadOnlySpan<T> Slice(int start, int length) | ||
{ | ||
return new ComplexArrayReadOnlySpan<T>(DataI.Slice(start, length), DataQ.Slice(start, length)); | ||
} | ||
|
||
public void CopyTo(ComplexArraySpan<T> destination) | ||
{ | ||
DataI.CopyTo(destination.DataI); | ||
DataQ.CopyTo(destination.DataQ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
using System.Diagnostics; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Qynit.Pulsewave; | ||
public readonly ref struct ComplexArraySpan<T> | ||
where T : unmanaged | ||
{ | ||
public Span<T> DataI { get; } | ||
public Span<T> DataQ { get; } | ||
public int Length => DataI.Length; | ||
internal ComplexArraySpan(Span<T> dataI, Span<T> dataQ) | ||
{ | ||
Debug.Assert(dataI.Length == dataQ.Length); | ||
DataI = dataI; | ||
DataQ = dataQ; | ||
} | ||
public static implicit operator ComplexArraySpan<T>(PooledComplexArray<T> source) | ||
{ | ||
return new ComplexArraySpan<T>(source.DataI, source.DataQ); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public ComplexArraySpan<T> Slice(int start) | ||
{ | ||
return new ComplexArraySpan<T>(DataI[start..], DataQ[start..]); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public ComplexArraySpan<T> Slice(int start, int length) | ||
{ | ||
return new ComplexArraySpan<T>(DataI.Slice(start, length), DataQ.Slice(start, length)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using CommunityToolkit.Diagnostics; | ||
|
||
namespace Qynit.Pulsewave; | ||
|
||
public sealed record class EnvelopeInfo | ||
{ | ||
private readonly double _indexOffset; | ||
public double IndexOffset | ||
{ | ||
get => _indexOffset; | ||
init | ||
{ | ||
if (value < 0 || value >= 1) | ||
{ | ||
ThrowHelper.ThrowArgumentOutOfRangeException(nameof(IndexOffset), value, $"Index offset of `EnvelopeInfo` should be in range [0, 1)."); | ||
} | ||
_indexOffset = IndexOffset; | ||
} | ||
} | ||
public double SampleRate { get; init; } | ||
|
||
public EnvelopeInfo(double indexOffset, double sampleRate) | ||
{ | ||
IndexOffset = indexOffset; | ||
SampleRate = sampleRate; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
namespace Qynit.Pulsewave; | ||
public interface IFilterNode | ||
using System.Numerics; | ||
|
||
namespace Qynit.Pulsewave; | ||
public interface IFilterNode<T> | ||
where T : unmanaged, IFloatingPointIeee754<T> | ||
{ | ||
void Initialize(); | ||
void Complete(); | ||
void AddPulse(IPulseShape shape, double tStart, double width, double plateau, double amplitude, double frequency, double phase, double referenceTime); | ||
void AddWaveform(Waveform waveform, double tShift, double amplitude, double frequency, double phase, double referenceTime); | ||
void AddWaveform(ComplexArrayReadOnlySpan<T> waveform, WaveformInfo waveformInfo, double amplitude, double frequency, double phase, double referenceTime); | ||
double SampleRate { get; } | ||
double TStart { get; } | ||
double TEnd { get; } | ||
string? Name { get; set; } | ||
IList<IFilterNode> Outputs { get; } | ||
IList<IFilterNode> Inputs { get; } | ||
IList<IFilterNode<T>> Outputs { get; } | ||
IList<IFilterNode<T>> Inputs { get; } | ||
} |
Oops, something went wrong.