ctioga 1.11.1

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 (103) hide show
  1. data/COPYING +340 -0
  2. data/ctioga/bin/ctable +28 -0
  3. data/ctioga/bin/ctioga +37 -0
  4. data/ctioga/doc/ctable.1 +156 -0
  5. data/ctioga/doc/ctioga.1 +2363 -0
  6. data/ctioga/examples/README +46 -0
  7. data/ctioga/examples/ctioga.gnuplot +4 -0
  8. data/ctioga/examples/ctioga_within_tioga.rb +53 -0
  9. data/ctioga/examples/ctiogarc.rb +24 -0
  10. data/ctioga/examples/include_1.rb +15 -0
  11. data/ctioga/examples/noise.dat +100 -0
  12. data/ctioga/examples/noise.rb +13 -0
  13. data/ctioga/examples/trig.csv +100 -0
  14. data/ctioga/examples/trig.dat +100 -0
  15. data/ctioga/examples/trig.rb +14 -0
  16. data/ctioga/examples/trigh.dat +100 -0
  17. data/ctioga/examples/trigh.rb +10 -0
  18. data/ctioga/examples/tutorial +763 -0
  19. data/ctioga/examples/tutorial.sh +269 -0
  20. data/ctioga/tests/README +14 -0
  21. data/ctioga/tests/axes.sh +40 -0
  22. data/ctioga/tests/basic.sh +11 -0
  23. data/ctioga/tests/draw.sh +24 -0
  24. data/ctioga/tests/histograms.sh +14 -0
  25. data/ctioga/tests/insets.sh +41 -0
  26. data/ctioga/tests/layouts.sh +29 -0
  27. data/ctioga/tests/legends.sh +113 -0
  28. data/ctioga/tests/styles.sh +43 -0
  29. data/ctioga/tests/test_style.sh +8 -0
  30. data/ctioga/tests/tests.sh +24 -0
  31. data/ctioga/tests/text_backend.sh +83 -0
  32. data/ctioga/tests/tioga_defaults.rb +18 -0
  33. data/lib/CTioga/axes.rb +904 -0
  34. data/lib/CTioga/backends.rb +88 -0
  35. data/lib/CTioga/boundaries.rb +224 -0
  36. data/lib/CTioga/ctable.rb +134 -0
  37. data/lib/CTioga/curve_style.rb +246 -0
  38. data/lib/CTioga/debug.rb +199 -0
  39. data/lib/CTioga/dimension.rb +133 -0
  40. data/lib/CTioga/elements.rb +17 -0
  41. data/lib/CTioga/elements/base.rb +84 -0
  42. data/lib/CTioga/elements/containers.rb +578 -0
  43. data/lib/CTioga/elements/curves.rb +368 -0
  44. data/lib/CTioga/elements/tioga_primitives.rb +440 -0
  45. data/lib/CTioga/layout.rb +595 -0
  46. data/lib/CTioga/legends.rb +29 -0
  47. data/lib/CTioga/legends/cmdline.rb +187 -0
  48. data/lib/CTioga/legends/item.rb +164 -0
  49. data/lib/CTioga/legends/style.rb +257 -0
  50. data/lib/CTioga/log.rb +73 -0
  51. data/lib/CTioga/movingarrays.rb +131 -0
  52. data/lib/CTioga/partition.rb +271 -0
  53. data/lib/CTioga/plot_style.rb +230 -0
  54. data/lib/CTioga/plotmaker.rb +1677 -0
  55. data/lib/CTioga/shortcuts.rb +69 -0
  56. data/lib/CTioga/structures.rb +82 -0
  57. data/lib/CTioga/styles.rb +140 -0
  58. data/lib/CTioga/themes.rb +581 -0
  59. data/lib/CTioga/themes/classical.rb +82 -0
  60. data/lib/CTioga/themes/demo.rb +63 -0
  61. data/lib/CTioga/themes/fits.rb +91 -0
  62. data/lib/CTioga/themes/mono.rb +33 -0
  63. data/lib/CTioga/tioga.rb +32 -0
  64. data/lib/CTioga/utils.rb +173 -0
  65. data/lib/MetaBuilder/Parameters/dates.rb +38 -0
  66. data/lib/MetaBuilder/Parameters/lists.rb +132 -0
  67. data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
  68. data/lib/MetaBuilder/Parameters/strings.rb +86 -0
  69. data/lib/MetaBuilder/Parameters/styles.rb +75 -0
  70. data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
  71. data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
  72. data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
  73. data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
  74. data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
  75. data/lib/MetaBuilder/descriptions.rb +603 -0
  76. data/lib/MetaBuilder/factory.rb +101 -0
  77. data/lib/MetaBuilder/group.rb +57 -0
  78. data/lib/MetaBuilder/metabuilder.rb +10 -0
  79. data/lib/MetaBuilder/parameter.rb +374 -0
  80. data/lib/MetaBuilder/parameters.rb +11 -0
  81. data/lib/MetaBuilder/qt4.rb +8 -0
  82. data/lib/SciYAG/Backends/backend.rb +379 -0
  83. data/lib/SciYAG/Backends/binner.rb +168 -0
  84. data/lib/SciYAG/Backends/cache.rb +102 -0
  85. data/lib/SciYAG/Backends/dataset.rb +158 -0
  86. data/lib/SciYAG/Backends/descriptions.rb +469 -0
  87. data/lib/SciYAG/Backends/filters.rb +25 -0
  88. data/lib/SciYAG/Backends/filters/average.rb +134 -0
  89. data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
  90. data/lib/SciYAG/Backends/filters/filter.rb +70 -0
  91. data/lib/SciYAG/Backends/filters/norm.rb +39 -0
  92. data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
  93. data/lib/SciYAG/Backends/filters/sort.rb +43 -0
  94. data/lib/SciYAG/Backends/filters/strip.rb +34 -0
  95. data/lib/SciYAG/Backends/filters/trim.rb +64 -0
  96. data/lib/SciYAG/Backends/gnuplot.rb +131 -0
  97. data/lib/SciYAG/Backends/math.rb +108 -0
  98. data/lib/SciYAG/Backends/mdb.rb +462 -0
  99. data/lib/SciYAG/Backends/multitext.rb +96 -0
  100. data/lib/SciYAG/Backends/source.rb +64 -0
  101. data/lib/SciYAG/Backends/text.rb +339 -0
  102. data/lib/SciYAG/backends.rb +16 -0
  103. metadata +191 -0
@@ -0,0 +1,64 @@
1
+ # trim.rb : A trimming filter
2
+ # Copyright (C) 2007 Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module SciYAG
19
+
20
+ module Backends
21
+
22
+ # Removes a given ratio of points
23
+ class TrimFilter < Filter
24
+ include Dobjects
25
+
26
+ describe "trim", "Trim", "Removes a certain number of points"
27
+
28
+ init_param 'nb', "Number", Float,
29
+ "Keep one point for each nb points"
30
+
31
+ def initialize(nb)
32
+ @nb = Float(nb)
33
+ if @nb <= 1
34
+ warn "Invalid parameter nb #{nb}, using 3"
35
+ @nb = 3
36
+ end
37
+ end
38
+
39
+ # There you go: a simple averageing filter.
40
+ def apply(f)
41
+ new_x = Dvector.new
42
+ new_y = Dvector.new
43
+ nb = 0.0
44
+ for x,y in f
45
+ if nb < 1
46
+ new_x << x
47
+ new_y << y
48
+ end
49
+ nb += 1
50
+ if nb >= @nb
51
+ nb -= @nb
52
+ end
53
+ end
54
+ return Function.new(new_x, new_y)
55
+ end
56
+
57
+ def apply!(f)
58
+ new_f = apply(f)
59
+ f.x.replace(new_f.x)
60
+ f.y.replace(new_f.y)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,131 @@
1
+ # gnuplot.rb: a backend to extract plots from gnuplot's files
2
+ # Copyright (C) 2007 Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+
19
+
20
+ require 'SciYAG/Backends/backend'
21
+ require 'Dobjects/Dvector'
22
+ require 'Dobjects/Function'
23
+
24
+ # To feed data to fancyread
25
+ require 'stringio'
26
+
27
+
28
+ module SciYAG
29
+
30
+ module Backends
31
+
32
+ class GnuplotBackend < Backend
33
+
34
+ include Dobjects
35
+
36
+ describe 'gnuplot', 'Gnuplot files', <<EOD
37
+ This backend hijacks gnuplot files to make extract data which they plot.
38
+ No information is taken about the style !
39
+ EOD
40
+
41
+ param_accessor :variables_overrides, 'vars',
42
+ "Variable overrides", {:type => :string},
43
+ "A colon-separated override of local variables, " +
44
+ "such as a=1;b=3;c=5"
45
+
46
+ param_accessor :range, 'range',
47
+ "Plotting X range", {:type => :string},
48
+ "The plotting X range, such as (0:2)"
49
+
50
+ # This is called by the architecture to get the data. It splits
51
+ # the set name into filename@cols, reads the file if necessary and
52
+ # calls get_data
53
+ def query_xy_data(set)
54
+ if set =~ /(.*)@(\d+)/
55
+ filename = $1
56
+ number = $2.to_i - 1
57
+ else
58
+ filename = set
59
+ number = 0
60
+ end
61
+ plots = run_gnuplot(filename)
62
+ return Function.new(*(plots[number]))
63
+ end
64
+
65
+ # Runs gnuplot on the file, and returns all datasets found
66
+ # inside it. Cached.
67
+ def run_gnuplot(filename)
68
+ date = File::mtime(filename)
69
+ # Get it from the cache !
70
+ get_cached_entry(filename, [], {:date => date}) do
71
+ debug "Running gnuplot on file #{filename}"
72
+ f = File.open(filename)
73
+ # We open a bidirectionnal connection to gnuplot:
74
+ gnuplot = IO.popen("gnuplot", "r+")
75
+ output = ""
76
+ gnuplot.puts "set term table"
77
+ for line in f
78
+ next if line =~ /set\s+term/
79
+ if @variables_overrides and line =~ /plot\s+/
80
+ debug "Found a plot, inserting variable overrides :#{@variables_overrides}"
81
+ line.gsub!(/plot\s+/, "#{@variables_overrides};plot ")
82
+ end
83
+ if @range and line =~ /plot\s+/
84
+ debug "Found a plot, inserting range :#{@range}"
85
+ line.gsub!(/plot\s+(\[[^\]]+\])?/,
86
+ "plot [#{@range}]")
87
+ end
88
+ gnuplot.print line
89
+ gnuplot.flush
90
+ output += slurp(gnuplot)
91
+ end
92
+ # Output a "\n" in the end.
93
+ gnuplot.puts ""
94
+ gnuplot.flush
95
+ gnuplot.close_write
96
+ # Then we get all that is remaining:
97
+ output += gnuplot.read
98
+ gnuplot.close
99
+
100
+ # Now, interaction with gnuplot is finished, and we want to
101
+ # parse that:
102
+ outputs = output.split("\n\n")
103
+ plots = []
104
+ for data in outputs
105
+ plots << Dvector.fancy_read(StringIO.new(data), [0,1])
106
+ end
107
+ # This block evaluates to plots:
108
+ plots
109
+ # DO NOT USE return !!!
110
+ end
111
+ end
112
+
113
+ # Gets all data from the given file until it blocks, and returns it.
114
+ def slurp(f, size = 10240)
115
+ str = ""
116
+ begin
117
+ while IO::select([f],[],[],0)
118
+ ret = f.readpartial(size)
119
+ if ret.empty?
120
+ return str
121
+ end
122
+ str += ret
123
+ end
124
+ end
125
+ return str
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,108 @@
1
+ # math.rb :This files provides the Math backend, which provides data based on
2
+ # mathematical formulas.
3
+ # Copyright (C) 2006 Vincent Fourmond
4
+
5
+ # This program is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 2 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+
20
+ require 'SciYAG/Backends/backend'
21
+ require 'Dobjects/Dvector'
22
+ require 'Dobjects/Function'
23
+
24
+ module SciYAG
25
+
26
+ module Backends
27
+
28
+ class MathBackend < Backend
29
+
30
+ include Dobjects
31
+ include Math
32
+
33
+ describe 'math', 'Mathematical functions', <<EOD
34
+ This backend returns computations of mathematical formulas.
35
+ EOD
36
+
37
+ param_accessor :samples, 'samples', "Samples",
38
+ {:type => :integer}, "The number of points"
39
+ param_accessor :x_range, 'xrange', "X Range",
40
+ {:type => :float_range}, "X range (a:b)"
41
+ param_accessor :t_range, 'trange', "T Range",
42
+ {:type => :float_range}, "T range (a:b) (parametric plot)"
43
+
44
+ param_accessor :log, 'log', "Logarithmic scale",
45
+ {:type => :boolean}, "Space samples logarithmically"
46
+
47
+ def initialize
48
+ super()
49
+ @samples = 100
50
+ @x_range = -10.0..10.0
51
+ @t_range = -10.0..10.0
52
+ @log = false
53
+ end
54
+
55
+ # This is called by the architecture to get the data. It splits
56
+ # the set name into func@range, reads the file if necessary and
57
+ # calls get_data.
58
+ def query_xy_data(set)
59
+ if set =~ /(.*)@(.*)/
60
+ @x_range = ParamRange.new($2)
61
+ set = $1
62
+ end
63
+ return Function.new(*get_data(set))
64
+ end
65
+
66
+
67
+ def get_data(set)
68
+ # Parametric plot with a : in the middle
69
+ if set =~ /:/
70
+ x_block, y_block = set.split(/:/).map { |s|
71
+ eval "proc {|t| #{s}}"
72
+ }
73
+
74
+ t_v = make_dvector(@t_range, @samples)
75
+ x_v = t_v.collect(&x_block)
76
+ y_v = t_v.collect(&y_block)
77
+ else
78
+ y_block = eval "proc {|x| #{set} }"
79
+ x_v = make_dvector(@x_range, @samples)
80
+ y_v = x_v.collect(&y_block)
81
+ end
82
+ return [x_v,y_v]
83
+ end
84
+
85
+ # Turns a Range and a number of points into a Dvector
86
+ def make_dvector(range, nb_points, log = @log)
87
+ n = nb_points -1
88
+ a = Dvector.new(nb_points) { |i|
89
+ i.to_f/(n.to_f)
90
+ }
91
+ # a is in [0:1] inclusive...
92
+ if log
93
+ delta = range.last/range.first
94
+ # delta is positive necessarily
95
+ a *= delta.log
96
+ a.exp!
97
+ a *= range.first
98
+ else
99
+ delta = range.last - range.first
100
+ a *= delta
101
+ a += range.first
102
+ end
103
+ return a
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,462 @@
1
+ # mdb.rb : a backend that can extract electrochemical data stored within
2
+ # MS Access databases created by the PAR PowerSuite utilities. I doubt
3
+ # it will be useful to many people, but, well no one knows !
4
+
5
+ # Copyright (C) 2006 Vincent Fourmond
6
+
7
+ # This program is free software; you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation; either version 2 of the License, or
10
+ # (at your option) any later version.
11
+
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program; if not, write to the Free Software
19
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+
21
+
22
+ require 'SciYAG/Backends/backend'
23
+ require 'Dobjects/Dvector'
24
+ require 'Dobjects/Function'
25
+
26
+ module SciYAG
27
+
28
+ module Backends
29
+
30
+ class MDBBackend < Backend
31
+
32
+ include Dobjects
33
+
34
+ describe 'mdb', 'PAR PowerSuite MDB files', <<EOD, false
35
+ Reads the MS Access files produced by the PAR electrochemistry suite.
36
+ Abstract Backend, needs to be redefined by children.
37
+ EOD
38
+
39
+
40
+
41
+ # The table of the file where we want to look for data.
42
+ attr_accessor :data_set_table_name
43
+
44
+ # The name of the X, Y and Z columns in the table.
45
+ attr_accessor :data_set_x_col, :data_set_y_col, :data_set_z_col
46
+
47
+ # If this attribute is set, the sets are further split
48
+ # for the different values of that column
49
+ attr_accessor :data_set_name_col
50
+
51
+
52
+ # A small class to hold a data set
53
+ class DataSet
54
+ include Dobjects
55
+
56
+ # The name of the dataset
57
+ attr_accessor :name
58
+
59
+ # The DataSetID property
60
+ attr_accessor :data_set_id
61
+
62
+ # Used internally to create a new dvector
63
+ def new_dvector
64
+ return Dvector.new(1000).resize(0)
65
+ end
66
+
67
+ # Returns the keys
68
+ def subsets
69
+ # return @x_data.keys.compact # removes the nil key...
70
+ return @subsets.compact
71
+ end
72
+
73
+ def initialize(name, data_set_id)
74
+ @name = name
75
+ @data_set_id = data_set_id
76
+
77
+ @subsets = []
78
+
79
+ @x_data = {} # A hash indexed on the set name
80
+ @y_data = {} # A hash indexed on the set name
81
+ @z_data = {} # A hash indexed on the set name
82
+ end
83
+
84
+ # Returns the x_data for the given set
85
+ def x_data(set = nil)
86
+ return @x_data[set]
87
+ end
88
+
89
+ # Returns the y_data for the given set
90
+ def y_data(set = nil)
91
+ return @y_data[set]
92
+ end
93
+
94
+ # Returns the z_data for the given set
95
+ def z_data(set = nil)
96
+ return @z_data[set]
97
+ end
98
+
99
+ # Adds a point to the set
100
+ def add_point(x,y,z,set = nil)
101
+ if not @x_data[set]
102
+ @subsets << set
103
+ @x_data[set] = new_dvector
104
+ @y_data[set] = new_dvector
105
+ @z_data[set] = new_dvector
106
+ end
107
+ @x_data[set] << x
108
+ @y_data[set] << y
109
+ @z_data[set] << z
110
+ end
111
+
112
+ end
113
+
114
+ def initialize
115
+ super()
116
+ @data_set_table_name = nil # has to be redefined by children
117
+ # A hash holding dataset_name -> DataSet object
118
+ @data_sets = {}
119
+ @data_set_id = [] # The same as upper,
120
+ # but with the set number
121
+
122
+ # The column for different sets
123
+ @data_set_name_col = "Series"
124
+
125
+ # The current database
126
+ @current_database = ""
127
+ end
128
+
129
+ # The separator for columns when reading the file.
130
+ COL_SEP = "###"
131
+ # The command to run mdb-export
132
+ MDB_EXPORT = "mdb-export"
133
+
134
+ # Reads a table and returns the table of lines, optionnally beginning
135
+ # with the header
136
+ def mdb_export_table(file, table, header = false)
137
+ cmd_line = "#{MDB_EXPORT} -Q -d '#{COL_SEP}' " +
138
+ if header
139
+ " "
140
+ else
141
+ "-H "
142
+ end +
143
+ "'#{file}' '#{table}' " +
144
+ "| sort -n" # we sort numerically with the program sort,
145
+ # decently faster
146
+ mdb = IO.popen(cmd_line)
147
+ l = mdb.readlines
148
+ return l
149
+ end
150
+
151
+ # Reads a table and turns it into a list of hashes
152
+ def mdb_table_to_hash(file, table)
153
+ pipe = IO.popen("#{MDB_EXPORT} -Q -d '#{COL_SEP}' -R '&&&&&' " +
154
+ "#{file} #{table}")
155
+ header = pipe.readline.chomp.split(COL_SEP)
156
+ raw_entries = pipe.read.split('&&&&&').map {|l| l.split(COL_SEP)}
157
+ entries = []
158
+ for raw_entry in raw_entries
159
+ entry = {}
160
+ header.length.times do |i|
161
+ entry[header[i]] = raw_entry[i]
162
+ end
163
+ entries << entry
164
+ end
165
+ return entries
166
+ end
167
+
168
+ # Turns the first line returned by mdb_export_table into a nice header
169
+ # hash
170
+ def turn_header_into_hash(lines)
171
+ header = lines.shift
172
+ header = header.split(COL_SEP)
173
+ header_hash = {}
174
+ i = 0
175
+ for name in header
176
+ header_hash[name] = i
177
+ i+=1
178
+ end
179
+ return header_hash
180
+ end
181
+
182
+ # Small helper function
183
+ def safe_float(x)
184
+ begin
185
+ Float(x)
186
+ rescue
187
+ 0.0/0.0
188
+ end
189
+ end
190
+
191
+ # Inherit the baseline stuff.
192
+ inherit_parameters :base_line
193
+
194
+ attr_reader :current_database
195
+ param :read_mdb_file, :current_database, "readdb", "Read Database",
196
+ {:type => :file, :filters => "Databases (*.mdb)"},
197
+ "Reads a database into memory"
198
+
199
+ # Reads the contents of a MDB file according to different defaults.
200
+ def read_mdb_file(file)
201
+ # First, make out the datasets from the file
202
+ if @current_database == file
203
+ return # We already read it !!
204
+ end
205
+ @current_database = file
206
+
207
+ data_sets_properties = mdb_table_to_hash(file, 'DataSetProperties')
208
+
209
+ prospective_data_sets = []
210
+ for set in data_sets_properties
211
+ prospective_data_sets << DataSet.new(set["DataSetName"],
212
+ set["DataSetID"])
213
+ end
214
+
215
+ # We turn it into another array indexed on DataSetID for faster lookup
216
+ @data_sets_ids = []
217
+ for ds in prospective_data_sets
218
+ @data_sets_ids[ds.data_set_id.to_i] = ds
219
+ end
220
+
221
+ # Now, we need to get data from the files
222
+ raise "@data_set_table_name has to be set" unless @data_set_table_name
223
+ raise "@data_set_y_col has to be set" unless @data_set_y_col
224
+ raise "@data_set_x_col has to be set" unless @data_set_x_col
225
+ raise "@data_set_z_col has to be set" unless @data_set_z_col
226
+
227
+ l = mdb_export_table(file, @data_set_table_name, true)
228
+ h = turn_header_into_hash(l)
229
+ if h.key?(@data_set_x_col) and
230
+ h.key?(@data_set_y_col) and
231
+ h.key?(@data_set_z_col)
232
+ # We first sort the data respective to the first column
233
+ # l.sort! do |a,b|
234
+ # i_a = a[0,a.index(COL_SEP)].to_i
235
+ # i_b = b[0,b.index(COL_SEP)].to_i
236
+ # i_a <=> i_b
237
+ # end
238
+ x_col = h[@data_set_x_col]
239
+ y_col = h[@data_set_y_col]
240
+ z_col = h[@data_set_z_col]
241
+ set_col = if @data_set_name_col
242
+ h[@data_set_name_col]
243
+ else
244
+ false
245
+ end
246
+ id = h["DataSetID"]
247
+
248
+
249
+ l.each do |l|
250
+ a = l.split(COL_SEP)
251
+ ds = @data_sets_ids[a[id].to_i]
252
+ x = safe_float(a[x_col])
253
+ y = safe_float(a[y_col])
254
+ z = safe_float(a[z_col])
255
+ if data_set_name_col
256
+ ds.add_point(x,y,z,a[set_col])
257
+ else
258
+ ds.add_point(x,y,z)
259
+ end
260
+ end
261
+
262
+ else
263
+ raise "It looks like #{@data_set_x_col} or #{@data_set_y_col} is " +
264
+ "missing from the table #{@data_set_table_name} of file #{file}"
265
+ end
266
+
267
+ # Now, we populate the data_sets hash
268
+ for ds in prospective_data_sets
269
+ @data_sets[ds.name] = ds
270
+ end
271
+ end
272
+
273
+ # Baseline stuff: we inherit it from the parents.
274
+
275
+
276
+ # This RE tells if a set looks like it might contain a subset.
277
+ # Slurps anything until the last @.
278
+ SUBSET_RE = /(.+)@(.+)/
279
+
280
+
281
+ # An internal function saying how to make a X,Y dataset from
282
+ # a x,y,z dataset. Can depend on many things. We just output
283
+ # X and Y cols here. Better be redefined by children.
284
+ def internal_to_external(x,y,z)
285
+ return Function.new(x,y)
286
+ end
287
+
288
+ # Internal function to get the data corresponding to one set.
289
+ def get_data(set)
290
+ if set =~ SUBSET_RE
291
+ s = string_to_set($1)
292
+ raise "Set #{$1} is unkown" unless s
293
+ return internal_to_external(s.x_data($2),
294
+ s.y_data($2),
295
+ s.z_data($2))
296
+ else
297
+ s = string_to_set(set)
298
+ raise "Set #{set} is unkown" unless s
299
+ return internal_to_external(s.x_data, s.y_data, s.z_data)
300
+ end
301
+ end
302
+
303
+ # This is called by the architecture to get the data. It splits
304
+ # the set name into filename@cols, reads the file if necessary and
305
+ # calls get_data
306
+ def query_xy_data(set)
307
+ data = get_data(set)
308
+ return Function.new(data.x.dup, data.y.dup)
309
+ end
310
+
311
+ # Transforms a string into a set. If the set starts with a #, it means
312
+ # we're directly interested in the number, not in the name. Can be a great
313
+ # deal useful, and much shorter...
314
+ def string_to_set(str)
315
+ if str =~ /^\#(\d+)$/
316
+ return @data_sets_ids[$1.to_i]
317
+ else
318
+ return @data_sets[str]
319
+ end
320
+ end
321
+
322
+ def expand_sets(set)
323
+ if s = string_to_set(set)
324
+ subs = s.subsets # we sort so that everything ids
325
+ # looking fine.
326
+ if subs.empty?
327
+ return [set]
328
+ else
329
+ return subs.collect {|s| "#{set}@#{s}"}
330
+ end
331
+ end
332
+ return [set]
333
+ end
334
+
335
+ # I think it is better not to take the cycles into account: they will
336
+ # hinder the display for no good reason.
337
+ def sets_available
338
+ return @data_sets.keys.sort
339
+ end
340
+
341
+ end
342
+
343
+ class MDBCyclicVoltammogram < MDBBackend
344
+
345
+ describe 'mdbcv', 'PAR PowerSuite MDB files, Cyclic Voltammetry', <<EOD
346
+ Reads the MS Access files produced by the PAR electrochemistry suite,
347
+ to extract cyclic voltammetry data.
348
+ EOD
349
+ def initialize
350
+ super
351
+ @data_set_table_name = "CVDataPoints_280"
352
+ @data_set_x_col = "E"
353
+ @data_set_y_col = "I"
354
+ @data_set_z_col = "T"
355
+
356
+ @type = :cv
357
+ @neg = true
358
+ end
359
+
360
+ # We import parameters from the parent
361
+ inherit_parameters :read_mdb_file, :base_line
362
+
363
+ param_accessor :type, 'type', "Type",
364
+ { :type => :list, :list => {
365
+ :cv => "I = f(E)",
366
+ :et => "E = f(t)",
367
+ :it => "I = f(t)"} },
368
+ "The kind of plot wanted"
369
+
370
+ param_accessor :neg, 'polarity', "Current polarity",
371
+ { :type => :boolean } , "True inverses current polarity (on by default)"
372
+
373
+ # Returns the set wanted. (x = E, y = I, z = T);
374
+ def internal_to_external(x,y,z)
375
+ if @neg
376
+ y = y.neg
377
+ end
378
+ case @type
379
+ when :cv
380
+ return Function.new(x,y)
381
+ when :et
382
+ return Function.new(z.sub(z.min).mul!(86400),x)
383
+ when :it
384
+ return Function.new(z.sub(z.min).mul!(86400),y)
385
+ end
386
+ end
387
+
388
+ end
389
+
390
+ class MDBPStep < MDBCyclicVoltammogram
391
+
392
+ describe 'mdbps', 'PAR PowerSuite MDB files, Potential Steps', <<EOD
393
+ Reads the MS Access files produced by the PAR electrochemistry suite,
394
+ to extract potential step data.
395
+ EOD
396
+
397
+ inherit_parameters :read_mdb_file, :base_line, :type, :neg
398
+
399
+ def initialize
400
+ super
401
+ @data_set_table_name = "CADataPoints_28"
402
+ @type = :it
403
+ end
404
+
405
+ end
406
+
407
+
408
+ class MDBEIS < MDBBackend
409
+
410
+ describe 'mdbeis', 'PAR PowerSuite MDB files, Impedance Spectroscopy', <<EOD
411
+ Reads the MS Access files produced by the PAR electrochemistry suite,
412
+ to extract impedance spectroscopy data.
413
+ EOD
414
+ def initialize
415
+ super
416
+ @data_set_table_name = "ImpDataPoints_20"
417
+ @data_set_x_col = "Freq"
418
+ @data_set_y_col = "Zre"
419
+ @data_set_z_col = "Zim"
420
+
421
+ @type = :wzm
422
+ end
423
+
424
+ # We import parameters from the parent
425
+ inherit_parameters :read_mdb_file, :base_line
426
+
427
+ param_accessor :type, 'type', "Type", {:type => :list,
428
+ :list => {
429
+ :wzm => "|Z| = f(f)",
430
+ :wzi => "Zi = f(f)",
431
+ :wzr => "Zr = f(f)",
432
+ :wphi => "Phi = f(f)",
433
+ :zrzi => "Zi = f(Zr)",
434
+ :yryi => "- Yi = f(Yr)",
435
+ }}, "The kind of plot wanted"
436
+
437
+ # Returns the set wanted. (x = Freq, y = Zre, z = Zim);
438
+ def internal_to_external(x,y,z)
439
+ case @type
440
+ when :wzm
441
+ return Function.new(x,y.pow(2).add!(z.pow(2)).sqrt)
442
+ when :wzi
443
+ return Function.new(x,z)
444
+ when :wzr
445
+ return Function.new(x,y)
446
+ when :zrzi
447
+ return Function.new(y,z)
448
+ when :wphi
449
+ return Function.new(x,z.div(y).atan!)
450
+ when :yryi
451
+ # We need to compute real and imaginary part of admittance
452
+ den = y**2 + z**2
453
+ return Function.new(y.div(den),
454
+ z.div(den))
455
+ end
456
+ end
457
+
458
+ end
459
+ end
460
+ end
461
+
462
+