tdms 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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