SignalFlux 0.4.0

dotnet add package SignalFlux --version 0.4.0
                    
NuGet\Install-Package SignalFlux -Version 0.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SignalFlux" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SignalFlux" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="SignalFlux" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SignalFlux --version 0.4.0
                    
#r "nuget: SignalFlux, 0.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SignalFlux@0.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SignalFlux&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=SignalFlux&version=0.4.0
                    
Install as a Cake Tool

SignalFlux — Engineering Computing for .NET

CI NuGet Version .NET License

Every engineering team I've worked with builds the same thing: a Signal class, a Measurement struct, a way to carry units, a timestamp type, some quality enum. Usually scattered across five repos, each with different design choices, none composable, all tied to a specific vendor or protocol. Data never flows between systems without custom glue code.

SignalFlux is the canonical domain model for engineering data on .NET — the vocabulary that makes different systems speak the same language. Not a math library, not a plotting engine, not a protocol. A shared type system that sits between your hardware and your analysis, giving every voltage reading, every temperature measurement, every experiment the same shape regardless of source.

Guiding principle: Never replace mature libraries. Standardize how they work together.

The Problem

// Typical codebase — every project reinvents:
class MySignal { public double[] Data; public double SampleRate; }  // no units
class Timestamp { public long Ticks; }                              // no formatting
struct Measurement { public double Value; public string Unit; }     // magic strings
enum Quality { OK, Bad }                                            // underspecified

No two implementations agree. Units are "V" in one place, "Volt" in another, null in a third. Timestamps mix UTC, local, and unspecified. Quality is boolean — either "good" or nothing. Signals have no metadata, no source tracking, no way to trace where data came from. Pipelines between acquisition, storage, analysis, and visualization need bespoke adapters at every seam.

SignalFlux eliminates the seam. It provides the shared types that acquisition, processing, storage, and presentation all agree on, so your pipeline code moves data instead of converting it.

How It Works

The entire domain model lives in SignalFluxbuilt on UnitsNet for compile-time-safe units with no other third-party runtime dependencies on either .NET 10 or .NET Standard 2.0.

┌──────────────────────────────────────────────────────────────┐
│                       SignalFlux                             │
│  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐  │
│  │   Signal<T>    │  │ Measurement<T> │  │     Event      │  │
│  │   .Samples     │  │   .Value       │  │   .Severity    │  │
│  │   .Frequency   │  │   .Timestamp   │  │   .Type        │  │
│  │   .Unit        │  │   .Unit        │  │   .Description │  │
│  │   .Quality     │  │   .Quality     │  │   .Source      │  │
│  │   .Tags        │  │   .Metadata    │  │                │  │
│  │   .Source      │  │                │  │                │  │
│  └────────────────┘  └────────────────┘  └────────────────┘  │
│  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐  │
│  │   Timestamp    │  │    Window      │  │   Result<T>    │  │
│  │   .Ticks       │  │   .Start       │  │   .IsSuccess   │  │
│  │   .DateTime    │  │   .Duration    │  │   .Value       │  │
│  │   .CompareTo   │  │   .End         │  │   .Error       │  │
│  │   .ToUnixMs()  │  │   .Contains()  │  │   .GetValue…() │  │
│  │                │  │   .Overlaps()  │  │   .GetDef…()   │  │
│  │                │  │                │  │                │  │
│  └────────────────┘  └────────────────┘  └────────────────┘  │
│  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐  │
│  │   Metadata     │  │    Range<T>    │  │    Quality     │  │
│  │   .With()      │  │   .Minimum     │  │   .Unknown     │  │
│  │   .ContainsKey │  │   .Maximum     │  │   .Good        │  │
│  │   .TryGetValue │  │   .Contains()  │  │   .Fair        │  │
│  │   .Keys        │  │                │  │   .Poor        │  │
│  │   .Values      │  │                │  │   .Bad         │  │
│  │   .Count       │  │                │  │   .Invalid     │  │
│  └────────────────┘  └────────────────┘  └────────────────┘  │
│  ┌────────────────────────────────────────────────────────┐  │
│  │     Experiment (signals + events + config + equip)     │  │
│  │      Session (experiments + annotations + replay)      │  │ 
│  └────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────┘

Every type is immutable by defaultWith*() methods return new instances. Thread-safe by construction. No defensive copies needed.

Units? No Magic Strings

Instead of passing "V", "Volt", or null through your pipeline:

// SignalFlux uses UnitsNet enums — compile-time checked, IntelliSense discoverable
new Signal<double>(data, 100, now, unit: ElectricPotentialUnit.Volt);
new Measurement<double>(24.5, now, unit: TemperatureUnit.DegreeCelsius);

The Unit property is typed System.Enum, accepting any of the ~100 unit enums UnitsNet defines. A Volt cannot accidentally be passed where DegreeCelsius is expected. No stringly-typed APIs.

Immutability Without Boilerplate

Every value type exposes With*() methods for safe ad-hoc modification:

var raw = new Signal<double>(samples, 100, now, unit: ElectricPotentialUnit.Volt);
// Re-express in different units without copying samples:
var mv   = raw.WithUnit(ElectricPotentialUnit.Millivolt);
// Adjust timing without reallocating the array:
var late = raw.WithStartTime(raw.StartTime + TimeSpan.FromSeconds(5));

Samples is ReadOnlyMemory<T> — the backing array is shared across all derived copies. Zero allocations on the hot path.

Streaming-First by Design

Signal generators expose both in-memory and streaming paths:

// In-memory — for scripts, small data, testing:
Signal<double> signal = generator.GenerateSignal(1000);

// Streaming — for live acquisition, large datasets:
await foreach (var chunk in generator.GenerateStreaming(4096, totalChunks: 100))
    await writer.WriteSignalAsync(chunk);

Storage readers follow the same pattern — CsvSignalReader supports both ReadAllSignalsAsync() (in-memory) and ReadStreamingAsync() (chunked). Same for IO streams. The calling code chooses the tradeoff.

Quality Is a First-Class Citizen

Data degrades. Sensors saturate. Networks drop packets. Quality is not a boolean:

Value Meaning
Unknown No quality assessment available
Good Full confidence in the data
Fair Usable but degraded (e.g., high noise)
Poor Questionable data, use with caution
Bad Known bad, but preserved for audit
Invalid Not valid under any interpretation

Every Signal<T> and Measurement<T> carries a Quality — no separate health channel needed.

Composition, Not Inheritance

// Assemble building blocks, don't extend base classes:
var exp = new Experiment(
    id: "EXP-001",
    signals: new Dictionary<string, object> { { "voltage", vSignal }, { "current", iSignal } },
    events: new[] { alarm },
    start: Timestamp.UtcNow,
    equipment: new[] { "DAQ-01" });

var session = new Session("SES-001", experiments: new[] { exp }, canReplay: true)
    .WithAnnotation("Pre-flight complete");

Experiment groups related signals and events. Session groups experiments and replay metadata. Both are sealed classes with structural equality — not base types to extend.

Use Cases

Data Acquisition Pipeline

Sensor → SignalFlux.Measurement → SignalFlux.Signal → SignalFlux.Experiment → SignalFlux.Storage

Every sample gets a timestamp, a unit, a quality assessment, and optional metadata before it ever touches disk. Downstream code reads the same types — no parse phase, no schema negotiation.

Multi-Vendor Telemetry

Two different DAQ systems produce Signal<double> with different units, rates, and start times. SignalFlux.TimeSeries provides alignment, resampling, and merging — across vendors, across protocols, across formats. The domain model is the shared contract.

Automated Test & Measurement

Test Script → SignalFlux.Generators.SineGenerator → Capture → Compare → PASS/FAIL

Generate stimulus signals, capture response via SignalFlux.IO, compare against expected ranges, archive the entire experiment (signals + events + config + equipment list) as a single Experiment object.

Regulatory & Audit Trails

Every Measurement has a Timestamp and Source. Every Experiment captures Operator, Equipment, and Configuration. Every Session carries annotations. Metadata attaches arbitrary key-value audit data to any domain object. The entire chain is preserved for review.

Technical Differentiators

vs. SignalFlux
Homemade Signal classes Zero-dependency core, immutable structs, IEquatable<T> everywhere, UnitsNet-typed units
Math.NET Math.NET is algorithmic (FFT, linear algebra). SignalFlux is a domain model. They complement each other: MathNet.Fourier.Forward(signal.Samples.Span)
OPC UA / MODBUS Protocol-specific. SignalFlux provides the protocol-independent types those adapters should produce
Vendor SDKs Tied to hardware. SignalFlux normalizes data from any source into one shape
Python (NumPy/Pandas) No static typing, no .NET interop. SignalFlux brings the same concept to .NET with Memory<T>, Span<T>, compile-time safety

Packages

Package Description
SignalFlux Core domain model: Signal<T>, Measurement<T>, Event, Experiment, Session, Result<T>, Metadata, Timestamp, Window, Range<T>, Quality
SignalFlux.TimeSeries Time-series operations: resampling, interpolation, alignment, windowing, statistics, downsampling
SignalFlux.Generators Signal generators: sine, square, noise, ramp, sawtooth, random walk
SignalFlux.IO Unified stream connection abstraction: TCP, UDP, Serial, Named Pipes with async, cancellation, timeouts
SignalFlux.Storage CSV streaming read/write, SQLite & Parquet backends, ISignalStore/IExperimentStore interfaces, SignalReplayer
SignalFlux.Protocols Protocol adapters for Modbus, MAVLink, and NMEA 0183 — bridge Signal<T> and Measurement<T> with real-world protocol data

Installation

dotnet add package SignalFlux
dotnet add package SignalFlux.TimeSeries
dotnet add package SignalFlux.Generators
dotnet add package SignalFlux.IO
dotnet add package SignalFlux.Storage
dotnet add package SignalFlux.Protocols

UnitsNet is automatically included as a dependency of SignalFlux. Add using UnitsNet.Units; to access typed unit enums like ElectricPotentialUnit.Volt, TemperatureUnit.DegreeCelsius, etc.

Quick Start

using SignalFlux;
using UnitsNet.Units;

// A precise moment in time
var now = Timestamp.UtcNow;

// A measurement — value, time, unit, quality
var meas = new Measurement<double>(
    value: 24.5,
    timestamp: now,
    unit: TemperatureUnit.DegreeCelsius,
    quality: Quality.Good);

// A time window
var window = new Window(now, TimeSpan.FromSeconds(10));

// A uniformly sampled signal
var samples = new double[] { 1.0, 1.5, 2.0, 2.5, 3.0 };
var signal = new Signal<double>(
    samples: samples.AsMemory(),
    frequency: 100,           // 100 Hz
    startTime: now,
    unit: ElectricPotentialUnit.Volt);

// Immutable copies via With*() pattern
var adjusted = signal.WithUnit(ElectricPotentialUnit.Millivolt).WithFrequency(200);

// Annotated metadata
var meta = new Metadata()
    .With("sensor", "PT-100")
    .With("location", "Reactor A");
var tagged = signal.WithMetadata(meta);

// An event during an experiment
var alarm = new Event(
    time: now,
    severity: EventSeverity.Warning,
    type: "OverTemp",
    description: "Temperature exceeded threshold",
    source: "Sensor-01");

// A result type — success or failure
var ok = Result<int>.Ok(42);
var fail = Result<int>.Fail("Sensor not responding");

// Group signals and events into an experiment
var experiment = new Experiment(
    id: "EXP-001",
    signals: new Dictionary<string, object> { { "voltage", signal } },
    events: new[] { alarm },
    start: now,
    tags: new Dictionary<string, string> { { "project", "qualification" } });

// Group experiments into a session
var session = new Session(
    id: "SES-001",
    experiments: new[] { experiment },
    canReplay: true);

Domain Model

Timestamp

A precise moment in time as a UTC tick count. Supports arithmetic, comparison, Unix conversion, and ISO 8601 formatting.

var t1 = Timestamp.UtcNow;
var t2 = Timestamp.FromUnixMilliseconds(1700000000000);
var elapsed = t1 - t2;           // TimeSpan
var later = t1 + TimeSpan.FromHours(1);
bool ordered = t1 < t2;          // comparison operators

Window

A half-open time interval [Start, End) with a positive duration.

var w = new Window(start, TimeSpan.FromSeconds(5));
bool inside = w.Contains(timestamp);
bool overlap = w.Overlaps(other);

Signal<T>

A uniformly sampled time-domain signal with frequency, unit, metadata, quality, and tags. All properties are immutable — use With*() for modifications.

var s = new Signal<double>(samples, frequency: 100, startTime: now, unit: ElectricPotentialUnit.Volt);
int count = s.Count;
TimeSpan dur = s.Duration;
TimeSpan dt = s.SampleInterval;
Timestamp end = s.EndTime;
var copy = s.WithSamples(newSamples).WithFrequency(200).WithUnit(ElectricPotentialUnit.Millivolt);

Measurement<T>

A single timestamped data point with value, unit, quality, and optional metadata.

var m = new Measurement<double>(98.6, Timestamp.UtcNow, unit: TemperatureUnit.DegreeFahrenheit);
var c = m.WithValue(37.0).WithUnit(TemperatureUnit.DegreeCelsius);

Event & EventSeverity

A notable occurrence at a specific time with severity (Debug, Info, Warning, Error, Critical), a machine-readable type, human-readable description, and optional source.

var e = new Event(
    time: Timestamp.UtcNow,
    severity: EventSeverity.Error,
    type: "CommsLost",
    description: "Connection to sensor timed out",
    source: "Gateway-01");

Experiment

Groups signals (keyed by name), events, configuration, equipment, and tags into a single experimental run.

var exp = new Experiment(
    id: "EXP-001",
    signals: dict,
    events: events,
    start: Timestamp.UtcNow,
    end: Timestamp.UtcNow + TimeSpan.FromMinutes(5),
    equipment: new[] { "DAQ-01", "Thermocouple-Bank" });

Session

Groups multiple experiments with annotations and a replay flag.

var ses = new Session("SES-001", experiments, canReplay: true)
    .WithAnnotation("Pre-flight check complete");

Result<T>

A discriminated union representing success or failure — no exceptions for control flow.

var r = Result<double>.Ok(3.14);
double val = r.GetValueOrThrow();          // throws if failed
double fallback = r.GetValueOrDefault(0);  // safe default

Range<T>

A closed interval [Minimum, Maximum] for comparable types.

var r = new Range<double>(0.0, 100.0);
bool ok = r.Contains(42.0);  // true

Metadata

An immutable-style IReadOnlyDictionary<string, object> key-value store. The With() method returns a new instance with the added entry.

var m = new Metadata()
    .With("sensor", "PT-100")
    .With("calibration_date", "2026-01-15");
object value = m["sensor"];

Quality

An enum describing data confidence: Unknown, Good, Fair, Poor, Bad, Invalid.

Supported Frameworks

  • .NET 10+: Optimized for maximum performance and Native AOT compatibility.
  • .NET Standard 2.0: Broad compatibility across legacy .NET platforms.

Roadmap

Phase 1 — Foundation ✓

  • SignalFlux, TimeSeries, Generators — all delivered

Phase 2 — Data Acquisition ✓

  • SignalFlux.IO: Unified IStreamConnection abstraction with TCP, UDP, Serial, Named Pipes adapters (async, cancellation, timeouts)
  • SignalFlux.Storage: CSV streaming read/write, ISignalStore/IExperimentStore interfaces, SQLite (SqliteSignalStore, SqliteExperimentStore), Parquet (ParquetSignalStore) storage backends
  • SignalReplayer: Replay signals from any ISignalStore with original timing support, integrated with Session.CanReplay flag
  • Samples: Live acquisition pipeline demo (simulated sensor → TCP → Signal → CSV + SQLite)

Phase 3 — Ecosystem (partial)

  • SignalFlux.Protocols: Protocol adapters for Modbus (ModbusSignalExtensions, ModbusConnectionAdapter), MAVLink v2 (MavlinkSignalExtensions, MavlinkConnectionAdapter), and NMEA 0183 (NmeaSentenceExtensions, NmeaConnectionAdapter) — scale/offset/clamping, Signal/Measurement conversion, runtime dialect loading
  • Visualization adapters: ScottPlot, OxyPlot (planned)

Phase 4 — Industry Integrations (planned)

  • OPC UA, CAN bus, device adapters, ML.NET / ONNX integration
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on SignalFlux:

Package Downloads
SignalFlux.IO

SignalFlux.IO provides a unified stream connection abstraction (IStreamConnection) for TCP, UDP, Serial, and Named Pipes with async, cancellation, and configurable timeouts.

SignalFlux.TimeSeries

SignalFlux.TimeSeries provides time-series operations for Signal<T> including resampling, interpolation, alignment, windowing, statistics, and downsampling.

SignalFlux.Generators

SignalFlux.Generators provides signal generators for simulation, test, and measurement including sine, square, noise, ramp, sawtooth, and random walk generators.

SignalFlux.Storage

SignalFlux.Storage provides CSV, SQLite, and Parquet storage backends for Signal<T> and Experiment, plus ISignalStore/IExperimentStore interfaces and SignalReplayer for timed replay.

SignalFlux.Protocols

SignalFlux.Protocol signalFlux provides protocol adapters for Modbus, MAVLink, and NMEA 0183, bridging Signal<T> and Measurement<T> with real-world protocol data.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.0 77 7/23/2026
0.3.0 268 7/13/2026

Provides the foundational domain model including Signal<T>, Measurement<T>, Event, Experiment, Session, Result<T>, Metadata, Timestamp, Window, Range<T>, and Quality, with typed units via UnitsNet and support for netstandard2.0 and net10.0.