缓冲区只会在缓冲区关闭之前释放所有值,这对于实时图表不是很有用。您必须将值拆分为不重叠的窗口 - 从给定的触发器开始,并在扫描条件完成时关闭 - 一个完整的扫描周期的窗口。不幸的是,窗口在启动时仍会为我们提供值,因此我们将不得不跳过触发器触发之前进入的所有值。
static IObservable<IObservable<T>> TriggeredSweep<T>(
this IObservable<T> source,
Func<T, bool> triggerCondition,
Func<T, bool> sweepEnd
)
{
source = source.Publish().RefCount();
return source.Window(() => source.Where(triggerCondition).Sample(source.Where(sweepEnd)))
.Select(s => s.SkipWhile(v => !triggerCondition(v)));
}
测试这一点的最佳方法是使用它所依据的示波器模型:
double period = 1000 / 0.5; //0.5 Hz
int cycles = 4; //cycles to display
int quantization = 100; //cycles to display
int amplitude = 10; //signal peak
int range = quantization * cycles; //full range
//Sine wave generator for n cycles
//makes tuple of (t, sin(t))
var source = Observable.Interval(TimeSpan.FromMilliseconds(period / range))
.Select(s => s % (range + 1))
.Select(s => Tuple.Create(s, amplitude * Math.Sin((double)s / ((double)range / (double)cycles) * 2 * Math.PI)));
source.TriggeredSweep(
value => value.Item2 > 5, //Trigger when Signal value > 5
value => value.Item1 / quantization >= cycles //end sweep when all cycles are done
)
.Subscribe(window =>
{
Console.Clear(); //Clear CRO Monitor
window.Subscribe(value =>
{
//Set (x, y)
Console.CursorLeft = (int)((double)value.Item1 / range * (Console.WindowWidth - 1));
Console.CursorTop = (int)((amplitude - value.Item2) / (2 * amplitude) * (Console.WindowHeight - 1));
//draw
Console.Write("x");
});
});
//prevent close
Console.ReadLine();
输出:
xxx xxxx xxx xxxx
xx x x x xx x x x
x x xx x x x xx x
xx x x x xx x x x
x x xx x x x xx x
x x x x x x x x
x x x x xx x x x
x x x x x x x
x x x x x x x
x x x x x x x
x x xx x x x xx
x x x x x x x
x x x x x x x
x x x x x x x x
x x x x x x x x
x x x x x x x x
xx x x x xx x x x
x x x x x x x x
x x x x x x x x
x x x x x x x x
x x x xx x x x xx
x x x x x x x x
x xx x x x xx x x
x x x xx x x x xx
x xx x x x xx x x
x x x xx x x x xx
xxxx xxx xxxx xxx
x x x x
我希望这段代码可能对使用 Rx 测试简单的信号处理功能有用。:)