ctioga 1.11.1

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