/// <summary> /// Finds patterns of A followed immediately by B (with no other intermediate events), occurring within (strictly less than) a given time duration /// </summary> /// <typeparam name="TKey">Key type</typeparam> /// <typeparam name="TPayload">Payload type</typeparam> /// <typeparam name="TResult">Result type</typeparam> /// <param name="stream">Input stream</param> /// <param name="firstMatch">First element in pattern</param> /// <param name="secondMatch">Second element in pattern</param> /// <param name="resultSelector">Compose result tuple using matching input events</param> /// <param name="withinDuration">Pattern occurs within (strictly less than) given time duration</param> /// <returns>Pattern result stream</returns> public static IStreamable <TKey, TResult> FollowedByImmediate <TKey, TPayload, TResult>( this IStreamable <TKey, TPayload> stream, Expression <Func <TPayload, bool> > firstMatch, Expression <Func <TPayload, bool> > secondMatch, Expression <Func <TPayload, TPayload, TResult> > resultSelector, long withinDuration) { if (withinDuration >= 0) { // Set match duration if (withinDuration < 2) { throw new Exception("Duration has to be at least 2 chronons"); } if (withinDuration > StreamEvent.MaxSyncTime) { throw new Exception("Duration is too large"); } stream = stream.AlterEventDuration(withinDuration - 1); } // Clip the stream with itself, to make it a signal stream var clippedStream = stream.Multicast(xs => xs.ClipEventDuration(xs)); // Shift the left side (first match) by one chronon, set right side (second match) to one chronon duration, join to find matches var result = clippedStream.Multicast(xs => xs.Where(firstMatch).ShiftEventLifetime(1).Join(xs.Where(secondMatch).AlterEventDuration(1), resultSelector)); return(result); }
/// <summary> /// Frequency filter. /// </summary> /// <param name="source">Input stream</param> /// <param name="period">Period of input stream</param> /// <param name="window">Window size</param> /// <param name="filter">Filter function to select in frequencies</param> /// <returns>Signal after frequency filter pass.</returns> public static IStreamable <Empty, Signal> BandPassFilter( this IStreamable <Empty, Signal> source, long period, long window, double low, double high ) { var bp = OnlineFilter.CreateBandpass(ImpulseResponse.Finite, period, low, high); return(source .Multicast(s => s .ShiftEventLifetime(window) .Join(s .TumblingWindowLifetime(window, window) .Aggregate(w => new BatchAggregate <Signal>()) .Select(input => FreqFilter(input, bp)) .SelectMany(e => e), l => l.ts, r => r.ts, (l, r) => r ) ) .ShiftEventLifetime(-window) ); }
public static IStreamable <TKey, StructTuple <TPayload, TPayload> > ComputeSignalChangeStream <TKey, TPayload>(this IStreamable <TKey, TPayload> input) { return (input.Multicast(xs => xs.ShiftEventLifetime(vs => vs + 1).Join(xs.AlterEventDuration(1), (l, r) => new StructTuple <TPayload, TPayload> { Item1 = l, Item2 = r }))); }
/// <summary> /// Resample signal from one frequency to a different one. /// </summary> /// <param name="source">Input stream</param> /// <param name="iperiod">Period of input signal stream</param> /// <param name="operiod">Period of output signal stream</param> /// <param name="offset">Offset</param> /// <returns>Result (output) stream in the new signal frequency</returns> public static IStreamable <Empty, Signal> Resample( this IStreamable <Empty, Signal> source, long iperiod, long operiod, long offset = 0) { return(source .Multicast(s => s.ClipEventDuration(s)) .Multicast(s => s .ShiftEventLifetime(1) .Join(s .AlterEventDuration(1), (l, r) => new { st = l.ts, sv = l.val, et = r.ts, ev = r.val })) .AlterEventLifetime(t => t - iperiod, iperiod) .Chop(offset, operiod) .HoppingWindowLifetime(1, operiod) .AlterEventDuration(operiod) .Select((t, e) => new Signal(t, ((e.ev - e.sv) * (t - e.st) / (e.et - e.st) + e.sv))) ); }
public static void Test() { IStreamable <Empty, SensorReading> inputStream = CreateStream(); const int threshold = 42; var crossedThreshold = inputStream.Multicast( input => { // Alter all events 1 sec in the future. var alteredForward = input.AlterEventLifetime(s => s + 1, 1); // Compare each event that occurs at input with the previous event. // Note that, this one works for strictly ordered, strictly (e.g 1 sec) regular streams. var filteredInputStream = input.Where(s => s.Value > threshold); var filteredAlteredStream = alteredForward.Where(s => s.Value < threshold); return(filteredInputStream.Join( filteredAlteredStream, (evt, prev) => new { evt.Time, Low = prev.Value, High = evt.Value })); }); crossedThreshold.ToStreamEventObservable().ForEachAsync(r => Console.WriteLine(r)).Wait(); }
/// <summary> /// Attach aggregate results back to events /// </summary> /// <param name="source">Input stream</param> /// <param name="sourceSelector">Selector for source stream</param> /// <param name="aggregate">Aggregate function</param> /// <param name="resultSelector">Result selector</param> /// <param name="window">Window size</param> /// <param name="period">Period</param> /// <param name="offset">Offset</param> /// <returns>Signal stream after attaching aggregate result</returns> public static IStreamable <TKey, TOutput> AttachAggregate <TKey, TPayload, TInput, TState, TResult, TOutput>( this IStreamable <TKey, TPayload> source, Func <IStreamable <TKey, TPayload>, IStreamable <TKey, TInput> > sourceSelector, Func <Window <TKey, TInput>, IAggregate <TInput, TState, TResult> > aggregate, Expression <Func <TPayload, TResult, TOutput> > resultSelector, long window, long period, long offset = 0 ) { return(source .Multicast(s => s .ShiftEventLifetime(offset) .Join(sourceSelector(s) .HoppingWindowLifetime(window, period, offset) .Aggregate(aggregate), resultSelector) .ShiftEventLifetime(-offset) ) ); }