001package jmri.jmrix.rps.reversealign;
002
003import java.io.File;
004import java.util.Objects;
005import javax.swing.BoxLayout;
006import javax.swing.JButton;
007import javax.swing.JCheckBox;
008import javax.swing.JComboBox;
009import javax.swing.JFileChooser;
010import javax.swing.JLabel;
011import javax.swing.JPanel;
012import javax.swing.JSeparator;
013import javax.swing.JTextField;
014import javax.vecmath.Point3d;
015import jmri.jmrix.rps.Algorithms;
016import jmri.jmrix.rps.Calculator;
017import jmri.jmrix.rps.Constants;
018import jmri.jmrix.rps.Distributor;
019import jmri.jmrix.rps.Measurement;
020import jmri.jmrix.rps.PositionFile;
021import jmri.jmrix.rps.Reading;
022import jmri.jmrix.rps.ReadingListener;
023import jmri.jmrix.rps.RpsSystemConnectionMemo;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027/**
028 * Gather RPS Readings and use them to align the detector.
029 * <p>
030 * Note that algorithms have a bias to find transmitters with positive Z
031 * coordinates. Since we're inverting the computation between receivers and
032 * transmitters, we also flip the sign of Z coordinates to keep this bias
033 * working for us.
034 *
035 * @author Bob Jacobsen Copyright (C) 2007
036 */
037public class AlignmentPanel extends javax.swing.JPanel
038        implements ReadingListener, Constants {
039
040    RpsSystemConnectionMemo memo;
041
042    public AlignmentPanel(RpsSystemConnectionMemo _memo) {
043        super();
044        memo = _memo;
045        Distributor.instance().addReadingListener(this);
046        nf = java.text.NumberFormat.getInstance();
047        nf.setMinimumFractionDigits(1);
048        nf.setMaximumFractionDigits(1);
049        nf.setGroupingUsed(false);
050    }
051
052    void initComponents() {
053        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
054
055        lines = new Line[NREADINGS];
056
057        // add alignment lines
058        for (int i = 0; i < NREADINGS; i++) {
059            lines[i] = new Line();
060            add(lines[i]);
061        }
062
063        // add bottom info
064        JPanel p;
065        p = new JPanel();
066        algorithm = Algorithms.algorithmBox();
067        p.add(algorithm);
068        p.add(new JLabel("Vs: "));
069        p.add(vs);
070        vs.setText("0.01345");
071        p.add(calc);
072        calc.addActionListener(event -> calculate());
073        add(p);
074
075        p = new JPanel();
076        p.add(new JLabel("X:"));
077        p.add(x1l);
078        p.add(x2l);
079        p.add(x3l);
080        p.add(x4l);
081        add(p);
082
083        p = new JPanel();
084        p.add(new JLabel("Y:"));
085        p.add(y1l);
086        p.add(y2l);
087        p.add(y3l);
088        p.add(y4l);
089        add(p);
090
091        p = new JPanel();
092        p.add(new JLabel("Z:"));
093        p.add(z1l);
094        p.add(z2l);
095        p.add(z3l);
096        p.add(z4l);
097        add(p);
098
099        p = new JPanel();
100        p.add(new JLabel("S:"));
101        p.add(stat1);
102        p.add(stat2);
103        p.add(stat3);
104        p.add(stat4);
105        add(p);
106
107        // file load, store
108        p = new JPanel();
109        JButton b1;
110        b1 = new JButton(Bundle.getMessage("ButtonStore_"));
111        b1.addActionListener(event -> store());
112        p.add(b1);
113
114        b1 = new JButton(Bundle.getMessage("ButtonLoad_"));
115        b1.addActionListener(event -> load());
116        p.add(b1);
117
118        add(new JSeparator());
119        add(p);
120
121        // load for debug
122        dummy3();
123    }
124
125    JFileChooser fci = jmri.jmrit.XmlFile.userFileChooser();
126
127    void load() {
128        try {
129            // request the filename from an open dialog
130            fci.rescanCurrentDirectory();
131            int retVal = fci.showOpenDialog(this);
132            // handle selection or cancel
133            if (retVal == JFileChooser.APPROVE_OPTION) {
134                File file = fci.getSelectedFile();
135                if (log.isInfoEnabled()) {
136                    log.info("located file {} for load", file);
137                }
138                // handle the file
139                PositionFile pf = new PositionFile();
140                pf.loadFile(file);
141                Point3d p;
142                Reading r;
143                for (int i = 0; i < NREADINGS; i++) {
144                    p = pf.getCalibrationPosition(i);
145                    if (p != null) {
146                        lines[i].setPoint(p);
147                    } else {
148                        lines[i].setPoint(new Point3d(0.f, 0.f, 0.f));
149                    }
150                }
151                for (int i = 0; i < NREADINGS; i++) {
152                    r = pf.getCalibrationReading(i);
153                    lines[i].reset();
154                    if (r != null) {
155                        lines[i].setReading(r);
156                    }
157                }
158            } else {
159                log.info("load cancelled in open dialog");
160            }
161        } catch (Exception e) {
162            log.error("exception during load: ", e);
163        }
164    }
165
166    void store() {
167        try {
168            // request the filename from an open dialog
169            fci.rescanCurrentDirectory();
170            int retVal = fci.showSaveDialog(this);
171            // handle selection or cancel
172            if (retVal == JFileChooser.APPROVE_OPTION) {
173                File file = fci.getSelectedFile();
174                if (log.isInfoEnabled()) {
175                    log.info("located file {} for load", file);
176                }
177                // handle the file
178                PositionFile pf = new PositionFile();
179                pf.prepare();
180                pf.setReceiver(1, getPoint(x1l, y1l, z1l), true);
181                pf.setReceiver(2, getPoint(x2l, y1l, z2l), true);
182                pf.setReceiver(3, getPoint(x3l, y1l, z3l), true);
183                pf.setReceiver(4, getPoint(x4l, y1l, z4l), true);
184
185                // save the measurements too
186                for (int i = 0; i < NREADINGS; i++) {
187                    Point3d p = lines[i].getPoint();
188                    p.z = -p.z;
189                    pf.setCalibrationPoint(p, lines[i].getReading());
190                }
191                pf.store(file);
192            } else {
193                log.info("load cancelled in open dialog");
194            }
195        } catch (Exception e) {
196            log.error("exception during load: ", e);
197        }
198    }
199
200    /**
201     * Service routine for finding a Point3d from input fields
202     * @param x X coordinate of resulting point
203     * @param y Y coordinate of resulting point
204     * @param z Z coordinate of resulting point
205     * @return point from coordinates
206     */
207    Point3d getPoint(JTextField x, JTextField y, JTextField z) {
208        float xval = Float.parseFloat(x.getText());
209        float yval = Float.parseFloat(y.getText());
210        float zval = Float.parseFloat(z.getText());
211        return new Point3d(xval, yval, zval);
212    }
213
214    void dummy1() {
215        lines[0].xl.setText("19");
216        lines[0].yl.setText("83.5");
217        lines[0].zl.setText("12.1");
218        lines[0].s1 = 1100.0;
219
220        lines[1].xl.setText("1.3");
221        lines[1].yl.setText("25");
222        lines[1].zl.setText("14.2");
223        lines[1].s1 = 4304.0;
224
225        lines[2].xl.setText("35.9");
226        lines[2].yl.setText("1.5");
227        lines[2].zl.setText("13.4");
228        lines[2].s1 = 5634.0;
229
230        lines[3].xl.setText("57.1");
231        lines[3].yl.setText("21.5");
232        lines[3].zl.setText("13.8");
233        lines[3].s1 = 4782.0;
234
235    }
236
237    void dummy2() {
238        lines[0].xl.setText("14.2");
239        lines[0].yl.setText("21.4");
240        lines[0].zl.setText("2");
241        lines[0].s1l.setText("" + 1274.1);
242        lines[0].s2l.setText("" + 3699.3);
243        lines[0].s3l.setText("" + 4764.2);
244        lines[0].s4l.setText("" + 4363.3);
245
246        lines[1].xl.setText("58.5");
247        lines[1].yl.setText("14");
248        lines[1].zl.setText("2");
249        lines[1].s1l.setText("" + 4389.4);
250        lines[1].s2l.setText("" + 1328.3);
251        lines[1].s3l.setText("" + 2326.8);
252        lines[1].s4l.setText("" + 3340.8);
253
254        lines[2].xl.setText("50.1");
255        lines[2].yl.setText("3.8");
256        lines[2].zl.setText("2");
257        lines[2].s1l.setText("" + 4005.7);
258        lines[2].s2l.setText("" + 1104);
259        lines[2].s3l.setText("" + 3166);
260        lines[2].s4l.setText("" + 4148.3);
261
262        lines[3].xl.setText("70.3");
263        lines[3].yl.setText("47.4");
264        lines[3].zl.setText("2");
265        lines[3].s1l.setText("" + 5741);
266        lines[3].s2l.setText("" + 3666.8);
267        lines[3].s3l.setText("" + 1652);
268        lines[3].s4l.setText("" + 1294.2);
269    }
270
271    void dummy3() {
272        int i;
273
274        i = 0;
275        lines[i].xl.setText("70.1");
276        lines[i].yl.setText("21.2");
277        lines[i].zl.setText("2");
278        lines[i].s1l.setText("" + 1282);
279        lines[i].s2l.setText("" + 3818);
280        lines[i].s3l.setText("" + 5209);
281        lines[i].s4l.setText("" + 4677);
282
283        i = 1;
284        lines[i].xl.setText("25.6");
285        lines[i].yl.setText("14.1");
286        lines[i].zl.setText("2");
287        lines[i].s1l.setText("" + 4412);
288        lines[i].s2l.setText("" + 1334);
289        lines[i].s3l.setText("" + 1956);
290        lines[i].s4l.setText("" + 3362);
291
292        i = 2;
293        lines[i].xl.setText("32.2");
294        lines[i].yl.setText("4.2");
295        lines[i].zl.setText("2");
296        lines[i].s1l.setText("" + 4010);
297        lines[i].s2l.setText("" + 1119);
298        lines[i].s3l.setText("" + 2876);
299        lines[i].s4l.setText("" + 4177);
300
301        i = 3;
302        lines[i].xl.setText("14.2");
303        lines[i].yl.setText("47.4");
304        lines[i].zl.setText("2");
305        lines[i].s1l.setText("" + 5762);
306        lines[i].s2l.setText("" + 3634);
307        lines[i].s3l.setText("" + 1607);
308        lines[i].s4l.setText("" + 1340);
309
310        i = 4;
311        lines[i].xl.setText("70.1");
312        lines[i].yl.setText("21.2");
313        lines[i].zl.setText("7.5");
314        lines[i].s1l.setText("" + 1083);
315        lines[i].s2l.setText("" + 3765);
316        lines[i].s3l.setText("" + 5247);
317        lines[i].s4l.setText("" + 4216);
318
319        i = 5;
320        lines[i].xl.setText("25.6");
321        lines[i].yl.setText("14.1");
322        lines[i].zl.setText("7.5");
323        lines[i].s1l.setText("" + 4328);
324        lines[i].s2l.setText("" + 1091);
325        lines[i].s3l.setText("" + 2312);
326        lines[i].s4l.setText("" + 3333);
327
328        i = 6;
329        lines[i].xl.setText("32.2");
330        lines[i].yl.setText("4.2");
331        lines[i].zl.setText("7.5");
332        lines[i].s1l.setText("" + 3959);
333        lines[i].s2l.setText("" + 831);
334        lines[i].s3l.setText("" + 3165);
335        lines[i].s4l.setText("" + 4148);
336
337        i = 7;
338        lines[i].xl.setText("14.2");
339        lines[i].yl.setText("47.4");
340        lines[i].zl.setText("7.5");
341        lines[i].s1l.setText("" + 5741);
342        lines[i].s2l.setText("" + 3599);
343        lines[i].s3l.setText("" + 1509);
344        lines[i].s4l.setText("" + 1119);
345    }
346
347    @Override
348    public void notify(Reading r) {
349        // update lines
350        for (Line line : lines) {
351            line.update(r);
352        }
353    }
354
355    double getVSound() {
356        try {
357            return Double.parseDouble(vs.getText());
358        } catch (Exception e) {
359            vs.setText("0.0344");
360            return 0.0344;
361        }
362    }
363
364    /**
365     * FInd x, y, z of sensors from inputs
366     */
367    void calculate() {
368        // for now, fixed offset
369
370        int offset = 0;
371
372        // read positions as sensor positions
373        // Assume 4 right now
374        // create a set of device locations
375        Point3d[] points = new Point3d[NREADINGS];
376
377        for (int i = 0; i < NREADINGS; i++) {
378            points[i] = lines[i].getPoint();
379        }
380
381        // Now, for each column of times, locate that sensor
382        {
383            // create a Reading
384            Reading r = getReading(NREADINGS, 0);
385
386            // calculate
387            Calculator c = Algorithms.newCalculator(points, getVSound(), offset, (String) Objects.requireNonNull(algorithm.getSelectedItem()));
388            Measurement m = c.convert(r);
389
390            // store
391            x1l.setText(nf.format(m.getX()));
392            y1l.setText(nf.format(m.getY()));
393            z1l.setText(nf.format(-m.getZ()));
394            stat1.setText(m.textCode());
395        }
396        {
397            // create a Reading
398            Reading r = getReading(NREADINGS, 1);
399
400            // calculate
401            Calculator c = Algorithms.newCalculator(points, getVSound(), offset, (String) algorithm.getSelectedItem());
402            Measurement m = c.convert(r);
403
404            // store
405            x2l.setText(nf.format(m.getX()));
406            y2l.setText(nf.format(m.getY()));
407            z2l.setText(nf.format(-m.getZ()));
408            stat2.setText(m.textCode());
409        }
410        {
411            // create a Reading
412            Reading r = getReading(NREADINGS, 2);
413
414            // calculate
415            Calculator c = Algorithms.newCalculator(points, getVSound(), offset, (String) algorithm.getSelectedItem());
416            Measurement m = c.convert(r);
417
418            // store
419            x3l.setText(nf.format(m.getX()));
420            y3l.setText(nf.format(m.getY()));
421            z3l.setText(nf.format(-m.getZ()));
422            stat3.setText(m.textCode());
423        }
424        {
425            // create a Reading
426            Reading r = getReading(NREADINGS, 3);
427
428            // calculate
429            Calculator c = Algorithms.newCalculator(points, getVSound(), offset, (String) algorithm.getSelectedItem());
430            Measurement m = c.convert(r);
431
432            // store
433            x4l.setText(nf.format(m.getX()));
434            y4l.setText(nf.format(m.getY()));
435            z4l.setText(nf.format(-m.getZ()));
436            stat4.setText(m.textCode());
437        }
438    }
439
440    Reading getReading(int n, int index) {
441        double[] vals = new double[NREADINGS];
442
443        for (int i = 0; i < NREADINGS; i++) {
444            vals[i] = lines[i].getTime(index);
445        }
446
447        return new Reading("(from alignment)", vals);
448    }
449
450    JTextField x1l = new JTextField(5);
451    JTextField y1l = new JTextField(5);
452    JTextField z1l = new JTextField(5);
453    JTextField stat1 = new JTextField(5);
454
455    JTextField x2l = new JTextField(5);
456    JTextField y2l = new JTextField(5);
457    JTextField z2l = new JTextField(5);
458    JTextField stat2 = new JTextField(5);
459
460    JTextField x3l = new JTextField(5);
461    JTextField y3l = new JTextField(5);
462    JTextField z3l = new JTextField(5);
463    JTextField stat3 = new JTextField(5);
464
465    JTextField x4l = new JTextField(5);
466    JTextField y4l = new JTextField(5);
467    JTextField z4l = new JTextField(5);
468    JTextField stat4 = new JTextField(5);
469
470    JTextField vs = new JTextField(5);
471    java.text.NumberFormat nf;
472
473    JComboBox<String> algorithm;
474
475    Line[] lines;
476
477    JButton calc = new JButton("Calculate");
478
479    /**
480     * Represent one line (DAQ element) of the operation
481     */
482    class Line extends JPanel {
483
484        Line() {
485            setLayout(new java.awt.FlowLayout());
486            add(new JLabel("Position:"));
487            add(xl);
488            add(yl);
489            add(zl);
490
491            add(acquire);
492            JButton reset = new JButton("Reset");
493            reset.addActionListener(event -> reset());
494
495            add(reset);
496            add(new JLabel("n:"));
497            add(nl);
498            add(new JLabel("Times:"));
499            add(s1l);
500            add(s2l);
501            add(s3l);
502            add(s4l);
503
504            n = 0;
505            s1 = s2 = s3 = s4 = 0.0;
506        }
507
508        double getTime(int i) {
509            switch (i) {
510                case 0:
511                    return Float.valueOf(s1l.getText()).intValue();
512                case 1:
513                    return Float.valueOf(s2l.getText()).intValue();
514                case 2:
515                    return Float.valueOf(s3l.getText()).intValue();
516                case 3:
517                    return Float.valueOf(s4l.getText()).intValue();
518                default:
519                    return -1;
520            }
521        }
522
523        Reading getReading() {
524            return new Reading("(from alignment)", new double[]{getTime(0), getTime(1), getTime(2), getTime(3)});
525        }
526
527        void reset() {
528            nl.setText("0");
529            n = 0;
530            s1l.setText("0");
531            s1 = 0;
532            s2l.setText("0");
533            s2 = 0;
534            s3l.setText("0");
535            s3 = 0;
536            s4l.setText("0");
537            s4 = 0;
538        }
539
540        void setReading(Reading r) {
541            s1l.setText("" + r.getValue(0));
542            s2l.setText("" + r.getValue(1));
543            s3l.setText("" + r.getValue(2));
544            s4l.setText("" + r.getValue(3));
545        }
546
547        void update(Reading r) {
548            if (Math.abs(r.getValue(0)) > MAXTIME
549                    || Math.abs(r.getValue(1)) > MAXTIME
550                    || Math.abs(r.getValue(2)) > MAXTIME
551                    || Math.abs(r.getValue(3)) > MAXTIME) {
552                return;
553            }
554
555            if (acquire.isSelected()) {
556                s1 = (n * s1 + r.getValue(0)) / (n + 1);
557                s1l.setText(nf.format(s1));
558
559                s2 = (n * s2 + r.getValue(1)) / (n + 1);
560                s2l.setText(nf.format(s2));
561
562                s3 = (n * s3 + r.getValue(2)) / (n + 1);
563                s3l.setText(nf.format(s3));
564
565                s4 = (n * s4 + r.getValue(3)) / (n + 1);
566                s4l.setText(nf.format(s4));
567
568                n++;
569                nl.setText("" + n);
570            }
571        }
572
573        /**
574         * Service routine for finding a Point3d from input fields
575         * @return Point from input coordinate values
576         */
577        Point3d getPoint() {
578            float xval = Float.parseFloat(xl.getText());
579            float yval = Float.parseFloat(yl.getText());
580            float zval = -Float.parseFloat(zl.getText());
581            return new Point3d(xval, yval, zval);
582        }
583
584        /**
585         * Service routine for setting the receiver input fields from a Point3d
586         * @param p Specific input point
587         */
588        void setPoint(Point3d p) {
589            xl.setText("" + p.x);
590            yl.setText("" + p.y);
591            zl.setText("" + p.z);
592        }
593
594        // data values
595        JCheckBox acquire = new JCheckBox("Acquire");
596
597        JTextField xl = new JTextField(5);
598        JTextField yl = new JTextField(5);
599        JTextField zl = new JTextField(5);
600
601        JTextField nl = new JTextField(5);
602        long n;
603
604        JTextField s1l = new JTextField(5);
605        JTextField s2l = new JTextField(5);
606        JTextField s3l = new JTextField(5);
607        JTextField s4l = new JTextField(5);
608        JTextField s5l = new JTextField(5);
609        JTextField s6l = new JTextField(5);
610        double s1, s2, s3, s4, s5, s6;
611    }
612
613    private final static Logger log = LoggerFactory.getLogger(AlignmentPanel.class);
614
615}