~ubuntu-branches/ubuntu/utopic/gpsprune/utopic

« back to all changes in this revision

Viewing changes to tim/prune/function/estimate/LearnParameters.java

  • Committer: Package Import Robot
  • Author(s): Mònica Ramírez Arceda
  • Date: 2013-05-15 10:26:51 UTC
  • mfrom: (1.1.9)
  • Revision ID: package-import@ubuntu.com-20130515102651-130bcw88wox3u0q0
Tags: 15-1
* New upstream version
  - "nautical mile" is added to unit selector (Closes: #639503)
* debian/control:
  - Add myself as an uploader
  - Standards-Version bump to 3.9.4, no changes needed
* debian/copyright:
  - Add required Copyright field to tim/prune/function/srtm/gen/ files
  - Update years
* debian/scripts/gpsprune:
  - Fix sed commands, when there is no host/port, proxyhost/proxyport
    variables must be empty.
    Thanks to Simó Albert i Beltran <sim6@probeta.net> and to
    <debian@activityworkshop.net> for the patch!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package tim.prune.function.estimate;
 
2
 
 
3
import java.awt.BorderLayout;
 
4
import java.awt.Component;
 
5
import java.awt.FlowLayout;
 
6
import java.awt.event.ActionEvent;
 
7
import java.awt.event.ActionListener;
 
8
import java.awt.event.AdjustmentEvent;
 
9
import java.awt.event.AdjustmentListener;
 
10
import java.awt.event.KeyAdapter;
 
11
import java.awt.event.KeyEvent;
 
12
import java.util.ArrayList;
 
13
 
 
14
import javax.swing.BorderFactory;
 
15
import javax.swing.Box;
 
16
import javax.swing.BoxLayout;
 
17
import javax.swing.JButton;
 
18
import javax.swing.JDialog;
 
19
import javax.swing.JLabel;
 
20
import javax.swing.JPanel;
 
21
import javax.swing.JScrollBar;
 
22
 
 
23
import tim.prune.App;
 
24
import tim.prune.GenericFunction;
 
25
import tim.prune.I18nManager;
 
26
import tim.prune.config.Config;
 
27
import tim.prune.data.DataPoint;
 
28
import tim.prune.data.Distance;
 
29
import tim.prune.data.RangeStats;
 
30
import tim.prune.data.Track;
 
31
import tim.prune.data.Unit;
 
32
import tim.prune.data.UnitSetLibrary;
 
33
import tim.prune.function.estimate.jama.Matrix;
 
34
import tim.prune.gui.ProgressDialog;
 
35
 
 
36
/**
 
37
 * Function to learn the estimation parameters from the current track
 
38
 */
 
39
public class LearnParameters extends GenericFunction implements Runnable
 
40
{
 
41
        /** Progress dialog */
 
42
        ProgressDialog _progress = null;
 
43
        /** Results dialog */
 
44
        JDialog _dialog = null;
 
45
        /** Calculated parameters */
 
46
        private ParametersPanel _calculatedParamPanel = null;
 
47
        private EstimationParameters _calculatedParams = null;
 
48
        /** Slider for weighted average */
 
49
        private JScrollBar _weightSlider = null;
 
50
        /** Label to describe position of slider */
 
51
        private JLabel _sliderDescLabel = null;
 
52
        /** Combined parameters */
 
53
        private ParametersPanel _combinedParamPanel = null;
 
54
        /** Combine button */
 
55
        private JButton _combineButton = null;
 
56
 
 
57
 
 
58
        /**
 
59
         * Inner class used to hold the results of the matrix solving
 
60
         */
 
61
        static class MatrixResults
 
62
        {
 
63
                public EstimationParameters _parameters = null;
 
64
                public double _averageErrorPc = 0.0; // percentage
 
65
        }
 
66
 
 
67
 
 
68
        /**
 
69
         * Constructor
 
70
         * @param inApp App object
 
71
         */
 
72
        public LearnParameters(App inApp)
 
73
        {
 
74
                super(inApp);
 
75
        }
 
76
 
 
77
        /** @return key for function name */
 
78
        public String getNameKey() {
 
79
                return "function.learnestimationparams";
 
80
        }
 
81
 
 
82
        /**
 
83
         * Begin the function
 
84
         */
 
85
        public void begin()
 
86
        {
 
87
                // Show progress bar
 
88
                if (_progress == null) {
 
89
                        _progress = new ProgressDialog(_parentFrame, getNameKey());
 
90
                }
 
91
                _progress.show();
 
92
                // Start new thread for the calculations
 
93
                new Thread(this).start();
 
94
        }
 
95
 
 
96
        /**
 
97
         * Run method in separate thread
 
98
         */
 
99
        public void run()
 
100
        {
 
101
                _progress.setMaximum(100);
 
102
                // Go through the track and collect the range stats for each sample
 
103
                ArrayList<RangeStats> statsList = new ArrayList<RangeStats>(20);
 
104
                Track track = _app.getTrackInfo().getTrack();
 
105
                final int numPoints = track.getNumPoints();
 
106
                final int sampleSize = numPoints / 30;
 
107
                int prevStartIndex = -1;
 
108
                for (int i=0; i<30; i++)
 
109
                {
 
110
                        int startIndex = i * sampleSize;
 
111
                        RangeStats stats = getRangeStats(track, startIndex, startIndex + sampleSize, prevStartIndex);
 
112
                        if (stats != null && stats.getMovingDistanceKilometres() > 1.0
 
113
                                && !stats.getTimestampsIncomplete()
 
114
                                && stats.getTotalDurationInSeconds() > 100
 
115
                                && stats.getStartIndex() > prevStartIndex)
 
116
                        {
 
117
                                // System.out.println("Got stats for " + stats.getStartIndex() + " to " + stats.getEndIndex());
 
118
                                statsList.add(stats);
 
119
                                prevStartIndex = stats.getStartIndex();
 
120
                        }
 
121
                        _progress.setValue(i);
 
122
                }
 
123
 
 
124
                // Check if we've got enough samples
 
125
                // System.out.println("Got a total of " + statsList.size() + " samples");
 
126
                if (statsList.size() < 10)
 
127
                {
 
128
                        _progress.dispose();
 
129
                        // Show error message, not enough samples
 
130
                        _app.showErrorMessage(getNameKey(), "error.learnestimationparams.failed");
 
131
                        return;
 
132
                }
 
133
                // Loop around, solving the matrices and removing the highest-error sample
 
134
                MatrixResults results = reduceSamples(statsList);
 
135
                if (results == null)
 
136
                {
 
137
                        _progress.dispose();
 
138
                        _app.showErrorMessage(getNameKey(), "error.learnestimationparams.failed");
 
139
                        return;
 
140
                }
 
141
 
 
142
                _progress.dispose();
 
143
 
 
144
                // Create the dialog if necessary
 
145
                if (_dialog == null)
 
146
                {
 
147
                        _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
 
148
                        _dialog.setLocationRelativeTo(_parentFrame);
 
149
                        // Create Gui and show it
 
150
                        _dialog.getContentPane().add(makeDialogComponents());
 
151
                        _dialog.pack();
 
152
                }
 
153
 
 
154
                // Populate the values in the dialog
 
155
                populateCalculatedValues(results);
 
156
                updateCombinedLabels(calculateCombinedParameters());
 
157
                _dialog.setVisible(true);
 
158
        }
 
159
 
 
160
 
 
161
        /**
 
162
         * Make the dialog components
 
163
         * @return the GUI components for the dialog
 
164
         */
 
165
        private Component makeDialogComponents()
 
166
        {
 
167
                JPanel dialogPanel = new JPanel();
 
168
                dialogPanel.setLayout(new BorderLayout());
 
169
 
 
170
                // main panel with a box layout
 
171
                JPanel mainPanel = new JPanel();
 
172
                mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
 
173
                // Label at top
 
174
                JLabel introLabel = new JLabel(I18nManager.getText("dialog.learnestimationparams.intro") + ":");
 
175
                introLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 
176
                introLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
 
177
                mainPanel.add(introLabel);
 
178
 
 
179
                // Panel for the calculated results
 
180
                _calculatedParamPanel = new ParametersPanel("dialog.estimatetime.results", true);
 
181
                _calculatedParamPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
 
182
                mainPanel.add(_calculatedParamPanel);
 
183
                mainPanel.add(Box.createVerticalStrut(14));
 
184
 
 
185
                mainPanel.add(new JLabel(I18nManager.getText("dialog.learnestimationparams.combine") + ":"));
 
186
                mainPanel.add(Box.createVerticalStrut(4));
 
187
                _weightSlider = new JScrollBar(JScrollBar.HORIZONTAL, 5, 1, 0, 11);
 
188
                _weightSlider.addAdjustmentListener(new AdjustmentListener() {
 
189
                        public void adjustmentValueChanged(AdjustmentEvent inEvent)
 
190
                        {
 
191
                                if (!inEvent.getValueIsAdjusting()) {
 
192
                                        updateCombinedLabels(calculateCombinedParameters());
 
193
                                }
 
194
                        }
 
195
                });
 
196
                mainPanel.add(_weightSlider);
 
197
                _sliderDescLabel = new JLabel(" ");
 
198
                _sliderDescLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
 
199
                mainPanel.add(_sliderDescLabel);
 
200
                mainPanel.add(Box.createVerticalStrut(12));
 
201
 
 
202
                // Results panel
 
203
                _combinedParamPanel = new ParametersPanel("dialog.learnestimationparams.combinedresults");
 
204
                _combinedParamPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
 
205
                mainPanel.add(_combinedParamPanel);
 
206
 
 
207
                dialogPanel.add(mainPanel, BorderLayout.NORTH);
 
208
 
 
209
                // button panel at bottom
 
210
                JPanel buttonPanel = new JPanel();
 
211
                buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
 
212
 
 
213
                // Combine
 
214
                _combineButton = new JButton(I18nManager.getText("button.combine"));
 
215
                _combineButton.addActionListener(new ActionListener() {
 
216
                        public void actionPerformed(ActionEvent arg0) {
 
217
                                combineAndFinish();
 
218
                        }
 
219
                });
 
220
                buttonPanel.add(_combineButton);
 
221
 
 
222
                // Cancel
 
223
                JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
 
224
                cancelButton.addActionListener(new ActionListener() {
 
225
                        public void actionPerformed(ActionEvent e) {
 
226
                                _dialog.dispose();
 
227
                        }
 
228
                });
 
229
                KeyAdapter escapeListener = new KeyAdapter() {
 
230
                        public void keyPressed(KeyEvent inE) {
 
231
                                if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}
 
232
                        }
 
233
                };
 
234
                _combineButton.addKeyListener(escapeListener);
 
235
                cancelButton.addKeyListener(escapeListener);
 
236
                buttonPanel.add(cancelButton);
 
237
                dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
 
238
                return dialogPanel;
 
239
        }
 
240
 
 
241
        /**
 
242
         * Construct a rangestats object for the selected range
 
243
         * @param inTrack track object
 
244
         * @param inStartIndex start index
 
245
         * @param inEndIndex end index
 
246
         * @param inPreviousStartIndex the previously used start index, or -1
 
247
         * @return range stats object or null if required information missing from this bit of the track
 
248
         */
 
249
        private RangeStats getRangeStats(Track inTrack, int inStartIndex, int inEndIndex, int inPreviousStartIndex)
 
250
        {
 
251
                // Check parameters
 
252
                if (inTrack == null || inStartIndex < 0 || inEndIndex <= inStartIndex || inStartIndex > inTrack.getNumPoints()) {
 
253
                        return null;
 
254
                }
 
255
                final int numPoints = inTrack.getNumPoints();
 
256
                int start = inStartIndex;
 
257
 
 
258
                // Search forward until a decent track point found for the start
 
259
                DataPoint p = inTrack.getPoint(start);
 
260
                while (start < numPoints && (p == null || p.isWaypoint() || !p.hasTimestamp() || !p.hasAltitude()))
 
261
                {
 
262
                        start++;
 
263
                        p = inTrack.getPoint(start);
 
264
                }
 
265
                if (inPreviousStartIndex >= 0 && start <= (inPreviousStartIndex + 10) // overlapping too much with previous range
 
266
                        || (start >= (numPoints - 10))) // starting too late in the track
 
267
                {
 
268
                        return null;
 
269
                }
 
270
 
 
271
                // Search forward (counting the radians) until a decent end point found
 
272
                double movingRads = 0.0;
 
273
                final double minimumRads = Distance.convertDistanceToRadians(1.0, UnitSetLibrary.UNITS_KILOMETRES);
 
274
                DataPoint prevPoint = inTrack.getPoint(start);
 
275
                int endIndex = start;
 
276
                boolean shouldStop = false;
 
277
                do
 
278
                {
 
279
                        endIndex++;
 
280
                        p = inTrack.getPoint(endIndex);
 
281
                        if (p != null && !p.isWaypoint())
 
282
                        {
 
283
                                if (!p.hasAltitude() || !p.hasTimestamp()) {return null;} // abort if no time/altitude
 
284
                                if (prevPoint != null && !p.getSegmentStart()) {
 
285
                                        movingRads += DataPoint.calculateRadiansBetween(prevPoint, p);
 
286
                                }
 
287
                        }
 
288
                        prevPoint = p;
 
289
                        if (endIndex >= numPoints) {
 
290
                                shouldStop = true; // reached the end of the track
 
291
                        }
 
292
                        else if (movingRads >= minimumRads && endIndex >= inEndIndex) {
 
293
                                shouldStop = true; // got at least a kilometre
 
294
                        }
 
295
                }
 
296
                while (!shouldStop);
 
297
 
 
298
                // Check moving distance
 
299
                if (movingRads >= minimumRads) {
 
300
                        return new RangeStats(inTrack, start, endIndex);
 
301
                }
 
302
                return null;
 
303
        }
 
304
 
 
305
        /**
 
306
         * Build an A matrix for the given list of RangeStats objects
 
307
         * @param inStatsList list of (non-null) RangeStats objects
 
308
         * @return A matrix with n rows and 5 columns
 
309
         */
 
310
        private static Matrix buildAMatrix(ArrayList<RangeStats> inStatsList)
 
311
        {
 
312
                final Unit METRES = UnitSetLibrary.UNITS_METRES;
 
313
                Matrix result = new Matrix(inStatsList.size(), 5);
 
314
                int row = 0;
 
315
                for (RangeStats stats : inStatsList)
 
316
                {
 
317
                        result.setValue(row, 0, stats.getMovingDistanceKilometres());
 
318
                        result.setValue(row, 1, stats.getGentleAltitudeRange().getClimb(METRES));
 
319
                        result.setValue(row, 2, stats.getSteepAltitudeRange().getClimb(METRES));
 
320
                        result.setValue(row, 3, stats.getGentleAltitudeRange().getDescent(METRES));
 
321
                        result.setValue(row, 4, stats.getSteepAltitudeRange().getDescent(METRES));
 
322
                        row++;
 
323
                }
 
324
                return result;
 
325
        }
 
326
 
 
327
        /**
 
328
         * Build a B matrix containing the observations (moving times)
 
329
         * @param inStatsList list of (non-null) RangeStats objects
 
330
         * @return B matrix with single column of n rows
 
331
         */
 
332
        private static Matrix buildBMatrix(ArrayList<RangeStats> inStatsList)
 
333
        {
 
334
                Matrix result = new Matrix(inStatsList.size(), 1);
 
335
                int row = 0;
 
336
                for (RangeStats stats : inStatsList)
 
337
                {
 
338
                        result.setValue(row, 0, stats.getMovingDurationInSeconds() / 60.0); // convert seconds to minutes
 
339
                        row++;
 
340
                }
 
341
                return result;
 
342
        }
 
343
 
 
344
        /**
 
345
         * Look for the maximum absolute value in the given column matrix
 
346
         * @param inMatrix matrix with only one column
 
347
         * @return row index of cell with greatest absolute value, or -1 if not valid
 
348
         */
 
349
        private static int getIndexOfMaxValue(Matrix inMatrix)
 
350
        {
 
351
                if (inMatrix == null || inMatrix.getNumColumns() > 1) {
 
352
                        return -1;
 
353
                }
 
354
                int index = 0;
 
355
                double currValue = 0.0, maxValue = 0.0;
 
356
                // Loop over the first column looking for the maximum absolute value
 
357
                for (int i=0; i<inMatrix.getNumRows(); i++)
 
358
                {
 
359
                        currValue = Math.abs(inMatrix.get(i, 0));
 
360
                        if (currValue > maxValue)
 
361
                        {
 
362
                                maxValue = currValue;
 
363
                                index = i;
 
364
                        }
 
365
                }
 
366
                return index;
 
367
        }
 
368
 
 
369
        /**
 
370
         * See if the given set of samples is sufficient for getting a descent solution (at least 3 nonzero values)
 
371
         * @param inRangeSet list of RangeStats objects
 
372
         * @param inRowToIgnore row index to ignore, or -1 to use them all
 
373
         * @return true if the samples look ok
 
374
         */
 
375
        private static boolean isRangeSetSufficient(ArrayList<RangeStats> inRangeSet, int inRowToIgnore)
 
376
        {
 
377
                int numGC = 0, numSC = 0, numGD = 0, numSD = 0; // number of samples with gentle/steep climb/descent values > 0
 
378
                final Unit METRES = UnitSetLibrary.UNITS_METRES;
 
379
                int i = 0;
 
380
                for (RangeStats stats : inRangeSet)
 
381
                {
 
382
                        if (i != inRowToIgnore)
 
383
                        {
 
384
                                if (stats.getGentleAltitudeRange().getClimb(METRES) > 0) {numGC++;}
 
385
                                if (stats.getSteepAltitudeRange().getClimb(METRES) > 0)  {numSC++;}
 
386
                                if (stats.getGentleAltitudeRange().getDescent(METRES) > 0) {numGD++;}
 
387
                                if (stats.getSteepAltitudeRange().getDescent(METRES) > 0)  {numSD++;}
 
388
                        }
 
389
                        i++;
 
390
                }
 
391
                return numGC > 3 && numSC > 3 && numGD > 3 && numSD > 3;
 
392
        }
 
393
 
 
394
        /**
 
395
         * Reduce the number of samples in the given list by eliminating the ones with highest errors
 
396
         * @param inStatsList list of stats
 
397
         * @return results in an object
 
398
         */
 
399
        private MatrixResults reduceSamples(ArrayList<RangeStats> inStatsList)
 
400
        {
 
401
                int statsIndexToRemove = -1;
 
402
                Matrix answer = null;
 
403
                boolean finished = false;
 
404
                double averageErrorPc = 0.0;
 
405
                while (!finished)
 
406
                {
 
407
                        // Remove the marked stats object, if any
 
408
                        if (statsIndexToRemove >= 0) {
 
409
                                inStatsList.remove(statsIndexToRemove);
 
410
                        }
 
411
 
 
412
                        // Build up the matrices
 
413
                        Matrix A = buildAMatrix(inStatsList);
 
414
                        Matrix B = buildBMatrix(inStatsList);
 
415
                        // System.out.println("Times in minutes are:\n" + B.toString());
 
416
 
 
417
                        // Solve (if possible)
 
418
                        try
 
419
                        {
 
420
                                answer = A.solve(B);
 
421
                                // System.out.println("Solved matrix with " + A.getNumRows() + " rows:\n" + answer.toString());
 
422
                                // Work out the percentage error for each estimate
 
423
                                Matrix estimates = A.times(answer);
 
424
                                Matrix errors = estimates.minus(B).divideEach(B);
 
425
                                // System.out.println("Errors: " + errors.toString());
 
426
                                averageErrorPc = errors.getAverageAbsValue();
 
427
                                // find biggest percentage error, remove it from list
 
428
                                statsIndexToRemove = getIndexOfMaxValue(errors);
 
429
                                if (statsIndexToRemove < 0)
 
430
                                {
 
431
                                        System.err.println("Something wrong - index is " + statsIndexToRemove);
 
432
                                        throw new Exception();
 
433
                                }
 
434
                                // Check whether removing this element would make the range set insufficient
 
435
                                finished = inStatsList.size() <= 25 || !isRangeSetSufficient(inStatsList, statsIndexToRemove);
 
436
                        }
 
437
                        catch (Exception e)
 
438
                        {
 
439
                                // Couldn't solve at all
 
440
                                System.out.println("Failed to reduce: " + e.getClass().getName() + " - " + e.getMessage());
 
441
                                return null;
 
442
                        }
 
443
                        _progress.setValue(20 + 80 * (30 - inStatsList.size())/5); // Counting from 30 to 25
 
444
                }
 
445
                // Copy results to an EstimationParameters object
 
446
                MatrixResults result = new MatrixResults();
 
447
                result._parameters = new EstimationParameters();
 
448
                result._parameters.populateWithMetrics(answer.get(0, 0) * 5, // convert from 1km to 5km
 
449
                        answer.get(1, 0) * 100.0, answer.get(2, 0) * 100.0,      // convert from m to 100m
 
450
                        answer.get(3, 0) * 100.0, answer.get(4, 0) * 100.0);
 
451
                result._averageErrorPc = averageErrorPc;
 
452
                return result;
 
453
        }
 
454
 
 
455
 
 
456
        /**
 
457
         * Populate the dialog's labels with the calculated values
 
458
         * @param inResults results of the calculations
 
459
         */
 
460
        private void populateCalculatedValues(MatrixResults inResults)
 
461
        {
 
462
                if (inResults == null || inResults._parameters == null)
 
463
                {
 
464
                        _calculatedParams = null;
 
465
                        _calculatedParamPanel.updateParameters(null, 0.0);
 
466
                }
 
467
                else
 
468
                {
 
469
                        _calculatedParams = inResults._parameters;
 
470
                        _calculatedParamPanel.updateParameters(_calculatedParams, inResults._averageErrorPc);
 
471
                }
 
472
        }
 
473
 
 
474
        /**
 
475
         * Combine the calculated parameters with the existing ones
 
476
         * according to the value of the slider
 
477
         * @return combined parameters
 
478
         */
 
479
        private EstimationParameters calculateCombinedParameters()
 
480
        {
 
481
                final double fraction1 = 1 - 0.1 * _weightSlider.getValue(); // slider left = value 0 = fraction 1 = keep current
 
482
                EstimationParameters oldParams = new EstimationParameters(Config.getConfigString(Config.KEY_ESTIMATION_PARAMS));
 
483
                return oldParams.combine(_calculatedParams, fraction1);
 
484
        }
 
485
 
 
486
        /**
 
487
         * Update the labels to show the combined parameters
 
488
         * @param inCombinedParams combined estimation parameters
 
489
         */
 
490
        private void updateCombinedLabels(EstimationParameters inCombinedParams)
 
491
        {
 
492
                // Update the slider description label
 
493
                String sliderDesc = null;
 
494
                final int sliderVal = _weightSlider.getValue();
 
495
                switch (sliderVal)
 
496
                {
 
497
                        case 0:  sliderDesc = I18nManager.getText("dialog.learnestimationparams.weight.100pccurrent"); break;
 
498
                        case 5:  sliderDesc = I18nManager.getText("dialog.learnestimationparams.weight.50pc"); break;
 
499
                        case 10: sliderDesc = I18nManager.getText("dialog.learnestimationparams.weight.100pccalculated"); break;
 
500
                        default:
 
501
                                final int currTenths = 10 - sliderVal, calcTenths = sliderVal;
 
502
                                sliderDesc = "" + currTenths + "0% " + I18nManager.getText("dialog.learnestimationparams.weight.current")
 
503
                                        + " + " + calcTenths + "0% " + I18nManager.getText("dialog.learnestimationparams.weight.calculated");
 
504
                }
 
505
                _sliderDescLabel.setText(sliderDesc);
 
506
                // And update all the combined params labels
 
507
                _combinedParamPanel.updateParameters(inCombinedParams);
 
508
                _combineButton.setEnabled(sliderVal > 0);
 
509
        }
 
510
 
 
511
        /**
 
512
         * React to the combine button, by saving the combined parameters in the config
 
513
         */
 
514
        private void combineAndFinish()
 
515
        {
 
516
                EstimationParameters params = calculateCombinedParameters();
 
517
                Config.setConfigString(Config.KEY_ESTIMATION_PARAMS, params.toConfigString());
 
518
                _dialog.dispose();
 
519
        }
 
520
}