#r "nuget: FSharp.Data.Tdms"
open FSharp.Data
[<Literal>]
let Path = "Experiment.tdms"
type Experiment = TdmsProvider<Path, WriteIndex = true>
let experiment = Experiment(Path)
experiment.Group1.Channel1.Data |> Array.iter (printfn "%f")
FSharp.Data.Tdms
FSharp.Data.Tdms
provides support for TDMS 2.0 files[1] from F# and C# on .NET 6.0.
From F# it allows you to access these in a type-safe manner through a generative type provider, while plain old functions and methods are available to both F# and C#.
FSharp.Data.Tdms
reads raw channel data from TDMS 2.0 files as arrays.
Reading DAQmx raw data
Defragmenting TDMS files
Writing TDMS files
FSharp.Data.Tdms
is licensed under an MIT License. Please refer to LICENSE
for its full text.
Using the generative type provider, given a TDMS file Experiment.tdms
with a channel containing floating-point values named Channel1
within a group named Group1
:
#r "nuget: FSharp.Data.Tdms"
open FSharp.Data
[<Literal>]
let Path = "Experiment.tdms"
type Experiment = TdmsProvider<Path, WriteIndex = true>
let experiment = Experiment(Path)
experiment.Group1.Channel1.Data |> Array.iter (printfn "%f")
Alternatively, use the functions in the FSharp.Data.Tdms
namespace, mainly those in the File
module:
#r "nuget: FSharp.Data.Tdms"
open FSharp.Data.Tdms
File.read "Experiment.tdms" true
|> File.tryRawData<float> "Group1" "Channel1"
|> Option.defaultValue [||]
|> Array.iter (printfn "%f")
These functions have asynchronous equivalents:
#r "nuget: FSharp.Data.Tdms"
open FSharp.Control.Tasks.NonAffine
open FSharp.Data.Tdms
task {
let! file = File.readAsync "Experiment.tdms" true
let! data = File.tryRawDataAsync<float> "Group1" "Channel1"
Option.defaultValue data
|> Array.iter (printfn "%f")
}
|> Async.AwaitTask
|> Async.RunSynchronously
When using FSharp.Data.Tdms
from C#, prefer the API for idiomatic C#:
using System;
using FSharp.Data.Tdms;
class Program
{
static void Main(string[] args)
{
File.Read("Experiment.tdms", true).TryGetRawData("Group1", "Channel1", out double[] data);
foreach (double sample in data)
{
Console.WriteLine(sample);
}
}
}
Or asynchronously:
using System;
using FSharp.Data.Tdms;
class Program
{
static async Task Main(string[] args)
{
var file = await File.ReadAsync("Experiment.tdms", true);
var data = await file.TryGetRawDataAsync("Group1", "Channel1");
foreach (double sample in data)
{
Console.WriteLine(sample);
}
}
}
Most TDMS 2.0 data types directly map to .NET 5.0 data types.
The first exception is tdsTypeTimeStamp
, which you can read as either FSharp.Data.Tdms.Timestamp
(this data type corresponds to the NI LabVIEW timestamp), System.DateTime
, System.DateTimeOffset
, or System.TimeSpan
.
In case of System.TimeSpan
, FSharp.Data.Tdms
returns the time elapsed since 01/01/1904 00:00:00.00 UTC
, as per this support document from NI.
The second exception is tdsTypeExtendedFloat
.
Since .NET 5.0 does not support 80-bit extended precision floating point numbers, FSharp.Data.Tdms
reads these as FSharp.Data.Tdms.Extended
values.
Name |
TDMS 2.0 data type |
.NET 5.0 data type |
F# alias |
C# alias |
Void |
|
|
None |
|
8-bit signed integer |
|
|
|
|
16-bit signed integer |
|
|
|
|
32-bit signed integer |
|
|
|
|
64-bit signed integer |
|
|
|
|
8-bit unsigned integer |
|
|
|
|
16-bit unsigned integer |
|
|
|
|
32-bit unsigned integer |
|
|
|
|
64-bit unsigned integer |
|
|
|
|
32-bit single-precision floating point |
|
|
|
|
64-bit double-precision floating point |
|
|
|
|
80-bit extended-precision floating point |
|
|
None |
|
Character string |
|
|
|
|
Boolean |
|
|
|
|
Timestamp |
|
None |
None |
|
32-bit single-precision floating point complex |
|
|
|
|
64-bit double-precision floating point complex |
|
None |
None |
The BenchmarkDotNet benchmarks in this section give an idea of the performance of FSharp.Data.Tdms
when compared to TDMSReader
, the only other TDMS 2.0 implementation which works on .NET 5.0.
Since TDMSReader
does not support reading TDMS index files, the benchmark disables this feature for FSharp.Data.Tdms
as well, for a fair comparison.
This means that FSharp.Data.Tdms
may perform better in practice for TDMS files with many raw data segments.
This benchmark reads 30,489 double-precision floating points from a segmented 3.1 MB TDMS 2.0 file.
BenchmarkDotNet=v0.12.1, OS=macOS 11.1 (20C69) [Darwin 20.2.0]
Intel Core i9-9980HK CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET Core SDK=5.0.101
[Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT DEBUG
.NET Core 5.0 : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
Job=.NET Core 5.0 Runtime=.NET Core 5.0
Method |
Mean |
Error |
StdDev |
Ratio |
RatioSD |
|
5.531 ms |
0.1049 ms |
0.0930 ms |
1.00 |
0.00 |
|
1.962 ms |
0.0378 ms |
0.0435 ms |
0.35 |
0.01 |
|
5.061 ms |
0.0503 ms |
0.0471 ms |
0.91 |
0.02 |
This benchmark reads a channel of 43,200 strings from a segmented 138.1 MB TDMS 2.0 file.
BenchmarkDotNet=v0.12.1, OS=macOS 11.1 (20C69) [Darwin 20.2.0]
Intel Core i9-9980HK CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET Core SDK=5.0.101
[Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT DEBUG
.NET Core 5.0 : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
Job=.NET Core 5.0 Runtime=.NET Core 5.0
Method |
Mean |
Error |
StdDev |
Ratio |
|
12.334 s |
0.2287 s |
0.2139 s |
1.00 |
|
4.400 s |
0.0370 s |
0.0328 s |
0.36 |
|
6.797 s |
0.0981 s |
0.0918 s |
0.55 |
This benchmark reads a channel of 779,297 double-precision floating points from a segmented 1.54 GB TDMS 2.0 file.
BenchmarkDotNet=v0.12.1, OS=macOS 11.1 (20C69) [Darwin 20.2.0]
Intel Core i9-9980HK CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET Core SDK=5.0.101
[Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT DEBUG
.NET Core 5.0 : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
Job=.NET Core 5.0 Runtime=.NET Core 5.0
Method |
Mean |
Error |
StdDev |
Ratio |
|
2.145 s |
0.0242 s |
0.0214 s |
1.00 |
|
1.103 s |
0.0123 s |
0.0103 s |
0.52 |
|
1.953 s |
0.0167 s |
0.0157 s |
0.91 |
Imposter syndrome disclaimer: I want your help. No really, I do.
There might be a little voice inside that tells you you’re not ready; that you need to do one more tutorial, or learn another framework, or write a few more blog posts before you can help me with this project.
I assure you, that’s not the case.
And you don’t just have to write code. You can help out by writing documentation, tests, or even by giving feedback about this work. (And yes, that includes giving feedback about the contribution guidelines.)
Thank you for contributing!
[1] National Instruments. 2019. The NI TDMS File Format. (January 2019). Retrieved January 12, 2019 from http://www.ni.com/white-paper/3727/en/
.