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}