tdms 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +10 -0
  3. data/LICENSE.txt +25 -0
  4. data/README.md +34 -0
  5. data/Rakefile +15 -0
  6. data/demo.rb +16 -0
  7. data/doc/data_types.txt +23 -0
  8. data/doc/example_disasm.txt +47 -0
  9. data/doc/tdms_format.txt +101 -0
  10. data/doc/usage.txt +46 -0
  11. data/lib/tdms.rb +8 -0
  12. data/lib/tdms/aggregate.rb +71 -0
  13. data/lib/tdms/channel.rb +100 -0
  14. data/lib/tdms/datatypes.rb +173 -0
  15. data/lib/tdms/document.rb +114 -0
  16. data/lib/tdms/path.rb +58 -0
  17. data/lib/tdms/property.rb +17 -0
  18. data/lib/tdms/segment.rb +12 -0
  19. data/lib/tdms/streaming.rb +82 -0
  20. data/test/build_fixtures/README.txt +25 -0
  21. data/test/build_fixtures/type_01_int8_one_segment.vi +0 -0
  22. data/test/build_fixtures/type_01_int8_three_segments.vi +0 -0
  23. data/test/build_fixtures/type_01_int8_two_channels_one_segment.vi +0 -0
  24. data/test/build_fixtures/type_02_int16_one_segment.vi +0 -0
  25. data/test/build_fixtures/type_02_int16_three_segments.vi +0 -0
  26. data/test/build_fixtures/type_02_int16_two_channels_one_segment.vi +0 -0
  27. data/test/build_fixtures/type_03_int32_one_segment.vi +0 -0
  28. data/test/build_fixtures/type_03_int32_three_segments.vi +0 -0
  29. data/test/build_fixtures/type_03_int32_two_channels_one_segment.vi +0 -0
  30. data/test/build_fixtures/type_04_int64_one_segment.vi +0 -0
  31. data/test/build_fixtures/type_04_int64_three_segments.vi +0 -0
  32. data/test/build_fixtures/type_04_int64_two_channels_one_segment.vi +0 -0
  33. data/test/build_fixtures/type_05_uint8_one_segment.vi +0 -0
  34. data/test/build_fixtures/type_05_uint8_three_segments.vi +0 -0
  35. data/test/build_fixtures/type_05_uint8_two_channels_one_segment.vi +0 -0
  36. data/test/build_fixtures/type_06_uint16_one_segment.vi +0 -0
  37. data/test/build_fixtures/type_06_uint16_three_segments.vi +0 -0
  38. data/test/build_fixtures/type_06_uint16_two_channels_one_segment.vi +0 -0
  39. data/test/build_fixtures/type_07_uint32_one_segment.vi +0 -0
  40. data/test/build_fixtures/type_07_uint32_three_segments.vi +0 -0
  41. data/test/build_fixtures/type_07_uint32_two_channels_one_segment.vi +0 -0
  42. data/test/build_fixtures/type_08_uint64_one_segment.vi +0 -0
  43. data/test/build_fixtures/type_08_uint64_three_segments.vi +0 -0
  44. data/test/build_fixtures/type_08_uint64_two_channels_one_segment.vi +0 -0
  45. data/test/build_fixtures/type_09_single_one_segment.vi +0 -0
  46. data/test/build_fixtures/type_09_single_three_segments.vi +0 -0
  47. data/test/build_fixtures/type_09_single_two_channels_one_segment.vi +0 -0
  48. data/test/build_fixtures/type_0a_double_one_segment.vi +0 -0
  49. data/test/build_fixtures/type_0a_double_three_segments.vi +0 -0
  50. data/test/build_fixtures/type_0a_double_two_channels_one_segment.vi +0 -0
  51. data/test/build_fixtures/type_20_string_one_segment.vi +0 -0
  52. data/test/build_fixtures/type_20_string_three_segments.vi +0 -0
  53. data/test/build_fixtures/type_20_string_two_channels_one_segment.vi +0 -0
  54. data/test/build_fixtures/type_21_boolean_one_segment.vi +0 -0
  55. data/test/build_fixtures/type_21_boolean_three_segments.vi +0 -0
  56. data/test/build_fixtures/type_21_boolean_two_channels_one_segment.vi +0 -0
  57. data/test/build_fixtures/type_44_datetime_one_segment.vi +0 -0
  58. data/test/build_fixtures/type_44_timestamp_three_segments.vi +0 -0
  59. data/test/build_fixtures/type_44_timestamp_two_channels_one_segment.vi +0 -0
  60. data/test/fixtures/example.tdms +0 -0
  61. data/test/fixtures/type_01_int8_one_segment.tdms +0 -0
  62. data/test/fixtures/type_01_int8_three_segments.tdms +0 -0
  63. data/test/fixtures/type_01_int8_two_channels_one_segment.tdms +0 -0
  64. data/test/fixtures/type_02_int16_one_segment.tdms +0 -0
  65. data/test/fixtures/type_02_int16_three_segments.tdms +0 -0
  66. data/test/fixtures/type_02_int16_two_channels_one_segment.tdms +0 -0
  67. data/test/fixtures/type_03_int32_one_segment.tdms +0 -0
  68. data/test/fixtures/type_03_int32_three_segments.tdms +0 -0
  69. data/test/fixtures/type_03_int32_two_channels_one_segment.tdms +0 -0
  70. data/test/fixtures/type_04_int64_one_segment.tdms +0 -0
  71. data/test/fixtures/type_04_int64_three_segments.tdms +0 -0
  72. data/test/fixtures/type_04_int64_two_channels_one_segment.tdms +0 -0
  73. data/test/fixtures/type_05_uint8_one_segment.tdms +0 -0
  74. data/test/fixtures/type_05_uint8_three_segments.tdms +0 -0
  75. data/test/fixtures/type_05_uint8_two_channels_one_segment.tdms +0 -0
  76. data/test/fixtures/type_06_uint16_one_segment.tdms +0 -0
  77. data/test/fixtures/type_06_uint16_three_segments.tdms +0 -0
  78. data/test/fixtures/type_06_uint16_two_channels_one_segment.tdms +0 -0
  79. data/test/fixtures/type_07_uint32_one_segment.tdms +0 -0
  80. data/test/fixtures/type_07_uint32_three_segments.tdms +0 -0
  81. data/test/fixtures/type_07_uint32_two_channels_one_segment.tdms +0 -0
  82. data/test/fixtures/type_08_uint64_one_segment.tdms +0 -0
  83. data/test/fixtures/type_08_uint64_three_segments.tdms +0 -0
  84. data/test/fixtures/type_08_uint64_two_channels_one_segment.tdms +0 -0
  85. data/test/fixtures/type_09_single_one_segment.tdms +0 -0
  86. data/test/fixtures/type_09_single_three_segments.tdms +0 -0
  87. data/test/fixtures/type_09_single_two_channels_one_segment.tdms +0 -0
  88. data/test/fixtures/type_0a_double_one_segment.tdms +0 -0
  89. data/test/fixtures/type_0a_double_three_segments.tdms +0 -0
  90. data/test/fixtures/type_0a_double_two_channels_one_segment.tdms +0 -0
  91. data/test/fixtures/type_19_single_with_unit_one_segment.tdms +0 -0
  92. data/test/fixtures/type_19_single_with_unit_three_segments.tdms +0 -0
  93. data/test/fixtures/type_19_single_with_unit_two_channels_one_segment.tdms +0 -0
  94. data/test/fixtures/type_1a_double_with_unit_one_segment.tdms +0 -0
  95. data/test/fixtures/type_1a_double_with_unit_three_segments.tdms +0 -0
  96. data/test/fixtures/type_1a_double_with_unit_two_channels_one_segment.tdms +0 -0
  97. data/test/fixtures/type_20_double_two_channels_one_segment.tdms +0 -0
  98. data/test/fixtures/type_20_string_one_segment.tdms +0 -0
  99. data/test/fixtures/type_20_string_three_segments.tdms +0 -0
  100. data/test/fixtures/type_20_string_two_channels_one_segment.tdms +0 -0
  101. data/test/fixtures/type_20_string_two_segments.tdms +0 -0
  102. data/test/fixtures/type_21_boolean_one_segment.tdms +0 -0
  103. data/test/fixtures/type_21_boolean_three_segments.tdms +0 -0
  104. data/test/fixtures/type_21_boolean_two_channels_one_segment.tdms +0 -0
  105. data/test/fixtures/type_44_timestamp_one_segment.tdms +0 -0
  106. data/test/fixtures/type_44_timestamp_three_segments.tdms +0 -0
  107. data/test/fixtures/type_44_timestamp_two_channels_one_segment.tdms +0 -0
  108. data/test/read_type_01_int8_test.rb +58 -0
  109. data/test/read_type_02_int16_test.rb +58 -0
  110. data/test/read_type_03_int32_test.rb +58 -0
  111. data/test/read_type_04_int64_test.rb +64 -0
  112. data/test/read_type_05_uint8_test.rb +56 -0
  113. data/test/read_type_06_uint16_test.rb +58 -0
  114. data/test/read_type_07_uint32_test.rb +58 -0
  115. data/test/read_type_08_uint64_test.rb +58 -0
  116. data/test/read_type_09_single_test.rb +58 -0
  117. data/test/read_type_0a_double_test.rb +58 -0
  118. data/test/read_type_19_single_with_unit_test.rb +58 -0
  119. data/test/read_type_1a_double_with_unit_test.rb +58 -0
  120. data/test/read_type_20_string_test.rb +60 -0
  121. data/test/read_type_21_boolean_test.rb +56 -0
  122. data/test/read_type_44_timestamp_test.rb +60 -0
  123. data/test/test_helper.rb +9 -0
  124. metadata +284 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1cdde94353fa626b85a4fa61044c5562baeca357
4
+ data.tar.gz: 6cd526130eecd98461297ce47a8a51ec11671801
5
+ SHA512:
6
+ metadata.gz: 8c3ed68005a5ec8c0c97f074d1096b15035cbe3174ebf6f212a0cc6225ba45bac582b3ee10a365c529bfa4ba9fa354c2b2365e84c6d9d32fa3777515ecb48cdc
7
+ data.tar.gz: 4443ac28b1267c32ac333c606f7c53bf11439d400076dec64ecf859163629a9c7139b4072fee5ce250643236092c509e80fc560ce1c5ce688a622d80f0a16053
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1
7
+ - 2.2
8
+ - 2.3
9
+ - 2.4
10
+
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2012, Mike Naberezny.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in the
11
+ documentation and/or other materials provided with the distribution.
12
+ * Neither the name of Maintainable Software, LLC. nor the names of its
13
+ contributors may be used to endorse or promote products derived from
14
+ this software without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,34 @@
1
+ # TDMS for Ruby
2
+
3
+ TDMS is a binary file format for measurement data. It was created
4
+ by National Instruments.
5
+
6
+ - [NI TDMS File Format](http://zone.ni.com/devzone/cda/tut/p/id/3727)
7
+ - [TDMS File Format Internal Structure](http://zone.ni.com/devzone/cda/tut/p/id/5696)
8
+
9
+ National Instruments software such as LabVIEW, DIAdem, and Measurement
10
+ Studio support reading and writing TDMS files. NI also provides a DLL
11
+ written in C for using TDMS files on Windows.
12
+
13
+ TDMS for Ruby was written to provide a convenient way to work with
14
+ TDMS files on Unix-like platforms.
15
+
16
+ ## Current State
17
+
18
+ This library is very early in development but is complete enough to
19
+ read the example TDMS file that comes with NI DIAdem.
20
+
21
+ - Segments with interleaved measurements are not yet supported.
22
+ - Segments with big endian data are not yet supported.
23
+ - Writing TDMS files is not yet supported.
24
+
25
+ ## Fork
26
+
27
+ Aaron Ten Clay has a [fork](https://github.com/aarontc/ruby_tdms) of this
28
+ library that claims to support the segment types above, along with reading
29
+ from streams instead of files, and other features.
30
+
31
+ ## Contributors
32
+
33
+ [Mike Naberezny](http://github.com/mnaberez) is the author of TDMS for
34
+ Ruby. Development is sponsored by [Maintainable](http://maintainable.com).
@@ -0,0 +1,15 @@
1
+ here = File.expand_path('..', __FILE__)
2
+ $LOAD_PATH.unshift File.join(here, 'lib')
3
+ $LOAD_PATH.unshift File.join(here, 'test')
4
+
5
+ desc "Run tests"
6
+ task :test do
7
+ require 'tdms'
8
+ require 'test_helper'
9
+
10
+ Dir.chdir(File.join(here, 'test')) do
11
+ Dir['**/*_test.rb'].each { |file| require file }
12
+ end
13
+ end
14
+
15
+ task :default => [:test]
data/demo.rb ADDED
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
2
+
3
+ require 'tdms'
4
+
5
+ filename = File.dirname(__FILE__) + "/test/fixtures/example.tdms"
6
+ doc = Tdms::File.parse(filename)
7
+
8
+ ch1 = doc.channels.find {|c| c.name == "StatisticsText"}
9
+ ch2 = doc.channels.find {|c| c.name == "Res_Noise_1"}
10
+
11
+ last = [ch1.values.size, ch2.values.size].min - 1
12
+
13
+ puts "#{ch1.name},#{ch2.name}"
14
+ 0.upto(last) do |i|
15
+ puts "#{ch1.values[i]},#{ch2.values[i]}"
16
+ end
@@ -0,0 +1,23 @@
1
+ Data Types
2
+
3
+ Identifier Name Length (bytes) Ruby
4
+
5
+ 0x00000000 tdsTypeVoid 1 Nil
6
+ 0x00000001 tdsTypeI8 1 Integer
7
+ 0x00000002 tdsTypeI16 2 Integer
8
+ 0x00000003 tdsTypeI32 4 Integer
9
+ 0x00000004 tdsTypeI64 8 Integer
10
+ 0x00000005 tdsTypeU8 1 Integer
11
+ 0x00000006 tdsTypeU16 2 Integer
12
+ 0x00000007 tdsTypeU32 4 Integer
13
+ 0x00000008 tdsTypeU64 8 Integer
14
+ 0x00000009 tdsTypeSingleFloat 4 Float
15
+ 0x0000000A tdsTypeDoubleFloat 8 Float
16
+ 0x0000000B tdsTypeExtendedFloat 10 ?
17
+ 0x00000019 tdsTypeSingleFloatWithUnit 4 Float
18
+ 0x0000001A tdsTypeDoubleFloatWithUnit 8 Float
19
+ 0x0000001B tdsTypeExtendedFloatWithUnit 10 ?
20
+ 0x00000020 tdsTypeString 4 len + n chars String
21
+ 0x00000021 tdsTypeBoolean 1 True, False
22
+ 0x00000044 tdsTypeTimeStamp 16 DateTime
23
+ 0xFFFFFFFF tdsTypeDAQmxRawData ? ?
@@ -0,0 +1,47 @@
1
+ EXAMPLE.tdms
2
+ Contains 4 segments
3
+
4
+
5
+ 000000 54 44 53 6d "TDSm" id tag
6
+ 000004 0E 00 00 00 0e is ToC flag, other 3 bytes unused
7
+ 000008 68 12 00 00 6812 is little endian for 0x1268 or 4713 (TDMS standard version). Other two bytes unused.
8
+ 00000C E3 88 20 00
9
+ 000010 00 00 00 00 => 64-bit unsigned little endian 0x00000000002088E3 is the next segment offset
10
+ 000014 E3 08 00 00
11
+ 000018 00 00 00 00 => 64-bit unsigned little endian 0x00000000000008E3 is the raw data offset
12
+ ( End of Lead-In )
13
+ ( 00001C is the offset of the next byte after the lead-in )
14
+ ( Next segment offset = 00001C + 2088E3 = 2088FF )
15
+ ( Raw data offset = 00001C + 0008E3 = 0008FF )
16
+
17
+ ( Start of Metadata )
18
+ 00001C 08 00 00 00 => 32-bit unsigned little endian 0x00000008 is the number of
19
+ new/changed objects in this segment (8 objects)
20
+ 000020 11 00 00 00 => 32-bit unsigned little endian 0x00000011 is the length of
21
+ the object's path string (11 bytes)
22
+ (Begin Path String)
23
+ 000024 2F 27 45 58 /'EX
24
+ 000028 41 4D 50 4C AMPL
25
+ 00002C 45 27 2F 27 E'/'
26
+ 000030 54 69 6D 65 Time
27
+ 000034 27 '
28
+ (Begin Length of Index)
29
+ 000034 14 00 00
30
+ 000038 0A 00 00 => 32-bit unsigned little endian 0x00000014 is the
31
+ raw data index
32
+ (Begin Data Type)
33
+ 000038 0A
34
+ 00003C 00 00 00 => 32-bit unsigned little endian 0x0000000A is the
35
+ data type (tdsTypeDoubleFloat)
36
+
37
+ (Begin Array Dimension)
38
+ 00003C 01
39
+ 000040 00 00 00 => 32-bit unsigned little endian 0x00000001 is the
40
+ array dimension (always 1)
41
+ (Begin Number of Values)
42
+ 000040 00
43
+ 000044 04 00 00 00
44
+ 000048 04 00 00 => 64-bit unsigned little endian 0x0000000000000400 is the
45
+ number of values (1024 values)
46
+ (Begin Total Size in Bytes)
47
+
@@ -0,0 +1,101 @@
1
+ http://zone.ni.com/devzone/cda/tut/p/id/5696
2
+
3
+ Object Hierarchy
4
+
5
+ Hierarchy Path
6
+
7
+ example_events.tdms (File) /
8
+ |
9
+ +-- Measured Data (Group) /'Measured Data'
10
+ | |
11
+ | +-- Amplitude Sweep (Channel) /'Measured Data'/'Amplitude Sweep'
12
+ | +-- Phase Sweep (Channel) /'Measured Data'/'Phase Sweep'
13
+ |
14
+ +-- Dr. T's Events (Group) /'Dr. T''s Events'
15
+ |
16
+ +-- Time (Channel) /'Dr. T''s Events'/'Time'
17
+ +-- Description (Channel) /'Dr. T''s Events'/'Description'
18
+
19
+ There are exactly 3 levels of objects in TDMS:
20
+ - File (every TDMS file must have one)
21
+ - Group (has many Channels, may have none)
22
+ - Channel (belongs to a Group)
23
+
24
+ Every object is identified by a string path. The only thing that
25
+ identifies an object as being a File, Group, or Channel is the
26
+ number of Segments in the string path:
27
+
28
+ Object Type Example Path Number of Path Segments
29
+ File / 0
30
+ Group /'Group' 1
31
+ Channel /'Group'/'Channel' 2
32
+
33
+
34
+ File Structure
35
+
36
+ File is divided into Segments.
37
+ Each segment contains one or more Objects
38
+ Every Object has a string Path
39
+
40
+
41
+ Segment
42
+
43
+ +-----------+------------+------------+----------------------------
44
+ | Lead-in | Metadata | Raw Data | Lead-in (Next Segment) ...
45
+ +-----------+------------+------------+----------------------------
46
+
47
+ Lead-in
48
+ +---------+--------+---------+--------------------+--------------------+---
49
+ | 4: TDSm | 4: ToC | 4: Vers | 8: Next Seg Offset | 8: Raw Data Offset | Metadata ...
50
+ +---------+--------+---------+--------------------+--------------------+---
51
+ 00 04 08 0C 14 1C
52
+
53
+
54
+ 00-03 4 bytes: TDMS identifier (always "TDSm")
55
+ 04-07 4 bytes: Table of contents (only first byte used)
56
+ Flags to indicate what is in the segment
57
+ 08-0B 4 bytes: TDMS version number (always 4713)
58
+ 0C-13 8 bytes: Offset of the next segment (little endian). Take the
59
+ absolute offset of the next byte after the lead-in and
60
+ add it to this number to find the next segment.
61
+ 14-1B 8 bytes: Offset of the raw data in this segment (little endian).
62
+ Also calculate it from next byte after the lead-in.
63
+
64
+
65
+ Metadata
66
+ +----------+-------------+---------+-------------------+--------------+-
67
+ | 4: Count | 4: Path Len | n: Path | 4: Raw data index | 4: Num Props |
68
+ +----------+-------------+---------+-------------------+--------------+-
69
+ 00 04 08
70
+
71
+ 4 bytes: Number of new/changed objects in this segment
72
+
73
+ For each object in the segment:
74
+ 4 bytes: Length of object's string path
75
+ n bytes: Object's string path
76
+
77
+ Index block or markers:
78
+ If no raw data in the segment:
79
+ 4 bytes: FF FF FF FF
80
+ Else if raw data index block exactly matches last segment:
81
+ 4 bytes: 00 00 00 00
82
+ Else:
83
+ 4 bytes: Length of raw data index block + 4
84
+ 4 bytes: Data type of the raw data in this object
85
+ 4 bytes: Dimension of raw data array (only first byte used, always 1)
86
+ 8 bytes: Number of values
87
+ 8 bytes: Total size in bytes (?) -- may not be here
88
+
89
+ Properties Block:
90
+
91
+ 4 bytes: Number of properties of this object
92
+
93
+ For each property:
94
+ 4 bytes: Length of property name
95
+ n bytes: Property name
96
+ 4 bytes: Data type of property value (only first byte used)
97
+ n bytes: Property value
98
+ String is 4 bytes for length, then bytes of string
99
+ Others are fixed number of bytes for value based on type
100
+
101
+ Raw Data
@@ -0,0 +1,46 @@
1
+ READING
2
+ =======
3
+
4
+ # display properties of a channel
5
+
6
+ group = segment.groups.find {|grp| grp.path == "/'EXAMPLE'" }
7
+ speed = group.channels.find {|ch| ch.path == "/'EXAMPLE'/'Time'" }
8
+ speed.properties.each_pair do |k,v|
9
+ puts k,v
10
+ end
11
+
12
+ # loop through a channel
13
+
14
+ group = segment.groups.find {|grp| grp.path == "/'EXAMPLE'" }
15
+ speed = group.channels.find {|ch| ch.path == "/'EXAMPLE'/'Time'" }
16
+ speed.values.each do |v|
17
+ puts v #=> float
18
+ end
19
+
20
+ # spreadsheet of two channels
21
+
22
+ group = segment.groups.find {|grp| grp.path == "/'EXAMPLE'" }
23
+
24
+ time = group.channels.find { |ch| ch.path == "/'EXAMPLE'/'Time'" }
25
+ speed = group.channels.find { |ch| ch.path == "/'EXAMPLE'/'Speed'" }
26
+
27
+ max = [time.values.size, speed.values.size].max - 1
28
+ 0.upto(max) do |i|
29
+ puts "%f,%f" % (time.values[i], speed.values[i])
30
+ end
31
+
32
+
33
+ WRITING
34
+ =======
35
+
36
+ tdms = Tdms::File.new("some filename")
37
+ seg = tdms.segment.build
38
+
39
+ group = seg.groups.build("foo")
40
+
41
+ chan = group.channels.build("bar")
42
+ chan.properties["Flux Capacitor"] = "On"
43
+ channel.values << 1.02
44
+ channel.values << 1.02
45
+
46
+ seg.save
@@ -0,0 +1,8 @@
1
+ require 'tdms/document'
2
+ require 'tdms/streaming'
3
+ require 'tdms/property'
4
+ require 'tdms/datatypes'
5
+ require 'tdms/segment'
6
+ require 'tdms/channel'
7
+ require 'tdms/path'
8
+ require 'tdms/aggregate'
@@ -0,0 +1,71 @@
1
+ module Tdms
2
+
3
+ class AggregateChannel
4
+ def initialize(channels=[])
5
+ @channels = channels
6
+ end
7
+
8
+ def path
9
+ @channels[0].path
10
+ end
11
+
12
+ def name
13
+ @channels[0].name
14
+ end
15
+
16
+ def data_type
17
+ @channels[0].data_type
18
+ end
19
+
20
+ def values
21
+ @values ||= AggregateChannelEnumerator.new(@channels)
22
+ end
23
+ end
24
+
25
+ class AggregateChannelEnumerator
26
+ include Enumerable
27
+
28
+ def initialize(channels)
29
+ @channels = channels
30
+ @offsets = []
31
+
32
+ size = 0
33
+ @channels.inject(0) do |size, channel|
34
+ @offsets << size
35
+ size += channel.values.size
36
+ end
37
+ end
38
+
39
+ def size
40
+ @size ||= @channels.inject(0) { |sum, chan| sum += chan.values.size }
41
+ end
42
+
43
+ def each
44
+ @channels.each do |channel|
45
+ channel.values.each { |value| yield value }
46
+ end
47
+ end
48
+
49
+ def [](i)
50
+ if (i < 0) || (i >= size)
51
+ raise RangeError, "Channel %s has a range of 0 to %d, got invalid index: %d" %
52
+ [@channels[0].path, size - 1, i]
53
+ end
54
+
55
+ channel, offset = nil, nil
56
+ j = @offsets.size - 1
57
+ @offsets.reverse_each do |o|
58
+ if i >= o
59
+ channel = @channels[j]
60
+ offset = @offsets[j]
61
+ break
62
+ else
63
+ j -= 1
64
+ end
65
+ end
66
+
67
+ channel.values[i - offset]
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,100 @@
1
+ module Tdms
2
+
3
+ class Channel < Object
4
+ attr_accessor :file, :path, :data_type_id, :dimension, :num_values,
5
+ :raw_data_pos
6
+
7
+ def name
8
+ path.channel
9
+ end
10
+
11
+ def values
12
+ @values ||= begin
13
+ klass = if data_type::LengthInBytes.nil?
14
+ StringChannelEnumerator
15
+ else
16
+ ChannelEnumerator
17
+ end
18
+
19
+ klass.new(self)
20
+ end
21
+ end
22
+
23
+ def data_type
24
+ @data_type ||= DataType.find_by_id(data_type_id)
25
+ end
26
+ end
27
+
28
+ class ChannelEnumerator
29
+ include Enumerable
30
+
31
+ def initialize(channel)
32
+ @channel = channel
33
+ end
34
+
35
+ def size
36
+ @size ||= @channel.num_values
37
+ end
38
+
39
+ def each
40
+ 0.upto(size - 1) { |i| yield self[i] }
41
+ end
42
+
43
+ def [](i)
44
+ if (i < 0) || (i >= size)
45
+ raise RangeError, "Channel %s has a range of 0 to %d, got invalid index: %d" %
46
+ [@channel.path, size - 1, i]
47
+ end
48
+
49
+ @channel.file.seek @channel.raw_data_pos + (i * @channel.data_type::LengthInBytes)
50
+ @channel.data_type.read_from_stream(@channel.file).value
51
+ end
52
+ end
53
+
54
+ class StringChannelEnumerator
55
+ include Enumerable
56
+
57
+ def initialize(channel)
58
+ @channel = channel
59
+
60
+ @index_pos = @channel.raw_data_pos
61
+ @data_pos = @index_pos + (4 * @channel.num_values)
62
+ end
63
+
64
+ def size
65
+ @size ||= @channel.num_values
66
+ end
67
+
68
+ def each
69
+ data_pos = @data_pos
70
+
71
+ 0.upto(size - 1) do |i|
72
+ index_pos = @index_pos + (4 * i)
73
+
74
+ @channel.file.seek index_pos
75
+ next_data_pos = @data_pos + @channel.file.read_u32
76
+
77
+ length = next_data_pos - data_pos
78
+
79
+ @channel.file.seek data_pos
80
+ yield @channel.file.read(length)
81
+
82
+ data_pos = next_data_pos
83
+ end
84
+ end
85
+
86
+ def [](i)
87
+ if (i < 0) || (i >= size)
88
+ raise RangeError, "Channel %s has a range of 0 to %d, got invalid index: %d" %
89
+ [@channel.path, size - 1, i]
90
+ end
91
+
92
+ inject(0) do |j, value|
93
+ return value if j == i
94
+ j += 1
95
+ end
96
+ end
97
+
98
+ end
99
+
100
+ end