• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeedu API Reference
  • KDE Home
  • Contact Us
 

kstars

  • sources
  • kde-4.12
  • kdeedu
  • kstars
  • kstars
  • ekos
align.cpp
Go to the documentation of this file.
1 /* Ekos Alignment Module
2  Copyright (C) 2013 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4  This application is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8  */
9 
10 #include <unistd.h>
11 #include <config-kstars.h>
12 #include <QProcess>
13 
14 #include "kstars.h"
15 #include "kstarsdata.h"
16 #include "align.h"
17 #include "dms.h"
18 #include "Options.h"
19 
20 #include <KMessageBox>
21 
22 #include "QProgressIndicator.h"
23 #include "indi/driverinfo.h"
24 #include "indi/indicommon.h"
25 
26 #include "fitsviewer/fitsviewer.h"
27 #include "fitsviewer/fitstab.h"
28 #include "fitsviewer/fitsview.h"
29 
30 #include <basedevice.h>
31 
32 namespace Ekos
33 {
34 
35 // 30 arcmiutes RA movement
36 const double Align::RAMotion = 0.5;
37 // Sidereal rate, degrees/s
38 const float Align::SIDRATE = 0.004178;
39 
40 Align::Align()
41 {
42  setupUi(this);
43 
44  currentCCD = NULL;
45  currentTelescope = NULL;
46  ccd_hor_pixel = ccd_ver_pixel = focal_length = aperture = -1;
47  decDeviation = azDeviation = altDeviation = 0;
48  useGuideHead = false;
49  canSync = false;
50 
51  connect(solveB, SIGNAL(clicked()), this, SLOT(capture()));
52  connect(stopB, SIGNAL(clicked()), this, SLOT(stopSolving()));
53  connect(measureAltB, SIGNAL(clicked()), this, SLOT(measureAltError()));
54  connect(measureAzB, SIGNAL(clicked()), this, SLOT(measureAzError()));
55  connect(polarR, SIGNAL(toggled(bool)), this, SLOT(checkPolarAlignment()));
56  connect(raBox, SIGNAL(textChanged( const QString & ) ), this, SLOT( checkLineEdits() ) );
57  connect(decBox, SIGNAL(textChanged( const QString & ) ), this, SLOT( checkLineEdits() ) );
58  connect(syncBoxesB, SIGNAL(clicked()), this, SLOT(copyCoordsToBoxes()));
59  connect(clearBoxesB, SIGNAL(clicked()), this, SLOT(clearCoordBoxes()));
60  connect(CCDCaptureCombo, SIGNAL(activated(int)), this, SLOT(checkCCD(int)));
61  connect(correctAltB, SIGNAL(clicked()), this, SLOT(correctAltError()));
62  connect(correctAzB, SIGNAL(clicked()), this, SLOT(correctAzError()));
63 
64  syncBoxesB->setIcon(KIcon("edit-copy"));
65  clearBoxesB->setIcon(KIcon("edit-clear"));
66 
67  raBox->setDegType(false); //RA box should be HMS-style
68 
69  appendLogText(i18n("Idle."));
70 
71  pi = new QProgressIndicator(this);
72 
73  controlLayout->addWidget(pi, 0, 2, 1, 1);
74 
75  exposureSpin->setValue(Options::alignExposure());
76 
77  altStage = ALT_INIT;
78  azStage = AZ_INIT;
79 
80  astrometryIndex[2.8] = "index-4200";
81  astrometryIndex[4.0] = "index-4201";
82  astrometryIndex[5.6] = "index-4202";
83  astrometryIndex[8] = "index-4203";
84  astrometryIndex[11] = "index-4204";
85  astrometryIndex[16] = "index-4205";
86  astrometryIndex[22] = "index-4206";
87  astrometryIndex[30] = "index-4207";
88  astrometryIndex[42] = "index-4208";
89  astrometryIndex[60] = "index-4209";
90  astrometryIndex[85] = "index-4210";
91  astrometryIndex[120] = "index-4211";
92  astrometryIndex[170] = "index-4212";
93  astrometryIndex[240] = "index-4213";
94  astrometryIndex[340] = "index-4214";
95  astrometryIndex[480] = "index-4215";
96  astrometryIndex[680] = "index-4216";
97  astrometryIndex[1000] = "index-4217";
98  astrometryIndex[1400] = "index-4218";
99  astrometryIndex[2000] = "index-4219";
100 
101 }
102 
103 Align::~Align()
104 {
105  delete(pi);
106 }
107 
108 
109 void Align::checkCCD(int ccdNum)
110 {
111  if (ccdNum <= CCDs.count())
112  currentCCD = CCDs.at(ccdNum);
113 
114  syncCCDInfo();
115 
116 }
117 
118 void Align::setCCD(ISD::GDInterface *newCCD)
119 {
120  CCDCaptureCombo->addItem(newCCD->getDeviceName());
121 
122  CCDs.append(static_cast<ISD::CCD *>(newCCD));
123 
124  checkCCD(0);
125 
126  CCDCaptureCombo->setCurrentIndex(0);
127 }
128 
129 void Align::setTelescope(ISD::GDInterface *newTelescope)
130 {
131  currentTelescope = (ISD::Telescope*) newTelescope;
132 
133  connect(currentTelescope, SIGNAL(numberUpdated(INumberVectorProperty*)), this, SLOT(updateScopeCoords(INumberVectorProperty*)));
134 
135  syncTelescopeInfo();
136 }
137 
138 void Align::syncTelescopeInfo()
139 {
140  INumberVectorProperty * nvp = currentTelescope->getBaseDevice()->getNumber("TELESCOPE_INFO");
141 
142  if (nvp)
143  {
144  INumber *np = IUFindNumber(nvp, "TELESCOPE_APERTURE");
145 
146  if (np && np->value > 0)
147  aperture = np->value;
148  else
149  {
150  np = IUFindNumber(nvp, "GUIDER_APERTURE");
151  if (np && np->value > 0)
152  aperture = np->value;
153  }
154 
155  np = IUFindNumber(nvp, "TELESCOPE_FOCAL_LENGTH");
156  if (np && np->value > 0)
157  focal_length = np->value;
158  else
159  {
160  np = IUFindNumber(nvp, "GUIDER_FOCAL_LENGTH");
161  if (np && np->value > 0)
162  focal_length = np->value;
163  }
164  }
165 
166  if (focal_length == -1 || aperture == -1)
167  {
168  controlBox->setEnabled(false);
169  modeBox->setEnabled(false);
170  KMessageBox::error(0, i18n("Telescope aperture and focal length are missing. Please check your driver settings and try again."));
171  return;
172  }
173 
174  if (ccd_hor_pixel != -1 && ccd_ver_pixel != -1 && focal_length != -1 && aperture != -1)
175  {
176  controlBox->setEnabled(true);
177  modeBox->setEnabled(true);
178  calculateFOV();
179  }
180 
181 
182  if (currentCCD && currentTelescope)
183  generateArgs();
184 
185  if (syncR->isEnabled() && (canSync = currentTelescope->canSync()) == false)
186  {
187  syncR->setEnabled(false);
188  slewR->setChecked(true);
189  appendLogText(i18n("Telescope does not support syncing."));
190  }
191 }
192 
193 
194 void Align::syncCCDInfo()
195 {
196  INumberVectorProperty * nvp = NULL;
197  int x,y;
198 
199  if (currentCCD == NULL)
200  return;
201 
202  if (useGuideHead)
203  nvp = currentCCD->getBaseDevice()->getNumber("GUIDER_INFO");
204  else
205  nvp = currentCCD->getBaseDevice()->getNumber("CCD_INFO");
206 
207  if (nvp)
208  {
209  INumber *np = IUFindNumber(nvp, "CCD_PIXEL_SIZE_X");
210  if (np && np->value >0)
211  ccd_hor_pixel = ccd_ver_pixel = np->value;
212 
213  np = IUFindNumber(nvp, "CCD_PIXEL_SIZE_Y");
214  if (np && np->value >0)
215  ccd_ver_pixel = np->value;
216 
217  np = IUFindNumber(nvp, "CCD_PIXEL_SIZE_Y");
218  if (np && np->value >0)
219  ccd_ver_pixel = np->value;
220  }
221 
222  ISD::CCDChip *targetChip = currentCCD->getChip(useGuideHead ? ISD::CCDChip::GUIDE_CCD : ISD::CCDChip::PRIMARY_CCD);
223 
224  targetChip->getFrame(&x,&y,&ccd_width,&ccd_height);
225 
226  if (ccd_hor_pixel == -1 || ccd_ver_pixel == -1)
227  {
228  controlBox->setEnabled(false);
229  modeBox->setEnabled(false);
230  KMessageBox::error(0, i18n("CCD pixel size is missing. Please check your driver settings and try again."));
231  return;
232  }
233 
234  if (ccd_hor_pixel != -1 && ccd_ver_pixel != -1 && focal_length != -1 && aperture != -1)
235  {
236  controlBox->setEnabled(true);
237  modeBox->setEnabled(true);
238  calculateFOV();
239  }
240 
241  if (currentCCD && currentTelescope)
242  generateArgs();
243 
244 }
245 
246 
247 void Align::calculateFOV()
248 {
249  // Calculate FOV
250  fov_x = 206264.8062470963552 * ccd_width * ccd_hor_pixel / 1000.0 / focal_length;
251  fov_y = 206264.8062470963552 * ccd_height * ccd_ver_pixel / 1000.0 / focal_length;
252 
253  fov_x /= 60.0;
254  fov_y /= 60.0;
255 
256  FOVOut->setText(QString("%1' x %2'").arg(fov_x, 0, 'g', 3).arg(fov_y, 0, 'g', 3));
257 
258  verifyIndexFiles();
259 
260 }
261 
262 bool Align::getAstrometryDataDir(QString &dataDir)
263 {
264  QFile confFile(Options::astrometryConfFile());
265 
266  if (confFile.open(QIODevice::ReadOnly) == false)
267  {
268  KMessageBox::error(0, i18n("Astrometry configuration file corrupted or missing: %1\nPlease set the configuration file full path in INDI options.", Options::astrometryConfFile()));
269  return false;
270  }
271 
272  QTextStream in(&confFile);
273  QString line;
274  QStringList confOptions;
275  while ( !in.atEnd() )
276  {
277  line = in.readLine();
278  if (line.startsWith("#"))
279  continue;
280 
281  confOptions = line.split(" ");
282  if (confOptions.size() == 2)
283  {
284  if (confOptions[0] == "add_path")
285  {
286  dataDir = confOptions[1];
287  return true;
288  }
289  }
290  }
291 
292  KMessageBox::error(0, i18n("Unable to find data dir in astrometry configuration file."));
293  return false;
294 }
295 
296 void Align::verifyIndexFiles()
297 {
298  static double last_fov_x=0, last_fov_y=0;
299 
300  if (last_fov_x == fov_x && last_fov_y == fov_y)
301  return;
302 
303  last_fov_x = fov_x;
304  last_fov_y = fov_y;
305  double fov_lower = 0.10 * fov_x;
306  double fov_upper = fov_x;
307  QStringList indexFiles;
308  QString astrometryDataDir;
309  bool indexesOK = true;
310 
311  if (getAstrometryDataDir(astrometryDataDir) == false)
312  return;
313 
314  QStringList nameFilter("*.fits");
315  QDir directory(astrometryDataDir);
316  QStringList indexList = directory.entryList(nameFilter);
317  QString indexSearch = indexList.join(" ");
318  QString startIndex, lastIndex;
319  unsigned int missingIndexes=0;
320 
321  foreach(float skymarksize, astrometryIndex.keys())
322  {
323  if (skymarksize >= fov_lower && skymarksize <= fov_upper)
324  {
325  indexFiles << astrometryIndex.value(skymarksize);
326 
327  if (indexSearch.contains(astrometryIndex.value(skymarksize)) == false)
328  {
329  if (startIndex.isEmpty())
330  startIndex = astrometryIndex.value(skymarksize);
331 
332  lastIndex = astrometryIndex.value(skymarksize);
333 
334  indexesOK = false;
335 
336  missingIndexes++;
337  }
338 
339  }
340  }
341 
342  if (indexesOK == false)
343  {
344  if (missingIndexes == 1)
345  KMessageBox::information(0, i18n("Index file %1 is missing. Astrometry.net would not be able to adequately solve plates until you install the missing index files. Download the index files from http://www.astrometry.net",
346  startIndex), i18n("Missing index files"), "ekos_missingindexes");
347  else
348  KMessageBox::information(0, i18n("Index files %1 to %2 are missing. Astrometry.net would not be able to adequately solve plates until you install the missing index files. Download the index files from http://www.astrometry.net",
349  startIndex, lastIndex), i18n("Missing index files"), "ekos_missingindexes");
350 
351  }
352 }
353 
354 void Align::generateArgs()
355 {
356  // -O overwrite
357  // -3 Expected RA
358  // -4 Expected DEC
359  // -5 Radius (deg)
360  // -L lower scale of image in arcminutes
361  // -H upper scale of image in arcmiutes
362  // -u aw set scale to be in arcminutes
363  // -W solution.wcs name of solution file
364  // apog1.jpg name of target file to analyze
365  //solve-field -O -3 06:40:51 -4 +09:49:53 -5 1 -L 40 -H 100 -u aw -W solution.wcs apod1.jpg
366 
367  double ra=0,dec=0, fov_lower, fov_upper;
368  QString ra_dms, dec_dms;
369  QString fov_low,fov_high;
370  QStringList solver_args;
371 
372  // let's strech the boundaries by 5%
373  fov_lower = ((fov_x < fov_y) ? (fov_x *0.95) : (fov_y *0.95));
374  fov_upper = ((fov_x > fov_y) ? (fov_x * 1.05) : (fov_y * 1.05));
375 
376  currentTelescope->getEqCoords(&ra, &dec);
377 
378  fov_low = QString("%1").arg(fov_lower);
379  fov_high = QString("%1").arg(fov_upper);
380 
381  getFormattedCoords(ra, dec, ra_dms, dec_dms);
382 
383  solver_args << "--no-verify" << "--no-plots" << "--no-fits2fits" << "--resort"
384  << "-O" << "-L" << fov_low << "-H" << fov_high << "-u" << "aw";
385 
386  if (raBox->isEmpty() == false && decBox->isEmpty() == false)
387  {
388  bool raOk(false), decOk(false), radiusOk(false);
389  dms ra( raBox->createDms( false, &raOk ) ); //false means expressed in hours
390  dms dec( decBox->createDms( true, &decOk ) );
391  int radius = 30;
392  QString message;
393 
394  if ( raOk && decOk )
395  {
396  //make sure values are in valid range
397  if ( ra.Hours() < 0.0 || ra.Hours() > 24.0 )
398  message = i18n( "The Right Ascension value must be between 0.0 and 24.0." );
399  if ( dec.Degrees() < -90.0 || dec.Degrees() > 90.0 )
400  message += '\n' + i18n( "The Declination value must be between -90.0 and 90.0." );
401  if ( ! message.isEmpty() )
402  {
403  KMessageBox::sorry( 0, message, i18n( "Invalid Coordinate Data" ) );
404  return;
405  }
406  }
407 
408  if (radiusBox->text().isEmpty() == false)
409  radius = radiusBox->text().toInt(&radiusOk);
410 
411  if (radiusOk == false)
412  {
413  KMessageBox::sorry( 0, message, i18n( "Invalid radius value" ) );
414  return;
415  }
416 
417  solver_args << "-3" << QString().setNum(ra.Degrees()) << "-4" << QString().setNum(dec.Degrees()) << "-5" << QString().setNum(radius);
418  }
419 
420  solverOptions->setText(solver_args.join(" "));
421 }
422 
423 void Align::checkLineEdits()
424 {
425  bool raOk(false), decOk(false);
426  raBox->createDms( false, &raOk );
427  decBox->createDms( true, &decOk );
428  if ( raOk && decOk )
429  generateArgs();
430 }
431 
432 void Align::copyCoordsToBoxes()
433 {
434  raBox->setText(ScopeRAOut->text());
435  decBox->setText(ScopeDecOut->text());
436 
437  checkLineEdits();
438 }
439 
440 void Align::clearCoordBoxes()
441 {
442  raBox->clear();
443  decBox->clear();
444 
445  generateArgs();
446 }
447 
448 bool Align::capture()
449 {
450  if (currentCCD == NULL)
451  return false;
452 
453  double seqExpose = exposureSpin->value();
454 
455  ISD::CCDChip *targetChip = currentCCD->getChip(useGuideHead ? ISD::CCDChip::GUIDE_CCD : ISD::CCDChip::PRIMARY_CCD);
456 
457  CCDFrameType ccdFrame = FRAME_LIGHT;
458 
459  if (currentCCD->isConnected() == false)
460  {
461  appendLogText(i18n("Error: Lost connection to CCD."));
462  return false;
463  }
464 
465  connect(currentCCD, SIGNAL(BLOBUpdated(IBLOB*)), this, SLOT(newFITS(IBLOB*)));
466 
467  targetChip->setCaptureMode(FITS_WCSM);
468 
469  targetChip->setFrameType(ccdFrame);
470 
471  targetChip->capture(seqExpose);
472 
473  Options::setAlignExposure(seqExpose);
474 
475  solveB->setEnabled(false);
476  stopB->setEnabled(true);
477  pi->startAnimation();
478 
479  appendLogText(i18n("Capturing image..."));
480 
481  return true;
482 }
483 
484 void Align::newFITS(IBLOB *bp)
485 {
486  // Ignore guide head if there is any.
487  if (!strcmp(bp->name, "CCD2"))
488  return;
489 
490  currentCCD->disconnect(this);
491 
492  appendLogText(i18n("Image received."));
493 
494  startSovling(QString(bp->label));
495 }
496 
497 void Align::startSovling(const QString &filename)
498 {
499  QStringList solverArgs;
500  double ra,dec;
501 
502  fitsFile = filename;
503 
504  currentTelescope->getEqCoords(&ra, &dec);
505 
506  targetCoord.setRA(ra);
507  targetCoord.setDec(dec);
508 
509  QString command = "solve-field";
510 
511  solverArgs = solverOptions->text().split(" ");
512  solverArgs << "-W" << "/tmp/solution.wcs" << filename;
513 
514  connect(&solver, SIGNAL(finished(int)), this, SLOT(solverComplete(int)));
515  connect(&solver, SIGNAL(readyReadStandardOutput()), this, SLOT(logSolver()));
516 
517  solverTimer.start();
518 
519  solver.start(command, solverArgs);
520 
521  appendLogText(i18n("Starting solver..."));
522 
523 }
524 
525 void Align::stopSolving()
526 {
527  solver.terminate();
528  solver.disconnect();
529  pi->stopAnimation();
530  stopB->setEnabled(false);
531  solveB->setEnabled(true);
532 
533  azStage = AZ_INIT;
534  altStage = ALT_INIT;
535 
536  ISD::CCDChip *targetChip = currentCCD->getChip(useGuideHead ? ISD::CCDChip::GUIDE_CCD : ISD::CCDChip::PRIMARY_CCD);
537 
538  // If capture is still in progress, let's stop that.
539  if (targetChip->isCapturing())
540  {
541  targetChip->abortExposure();
542  appendLogText(i18n("Capture aborted."));
543  }
544  else
545  {
546  int elapsed = (int) round(solverTimer.elapsed()/1000.0);
547  appendLogText(i18np("Solver aborted after %1 second.", "Solver aborted after %1 seconds", elapsed));
548  }
549 }
550 
551 void Align::solverComplete(int exist_status)
552 {
553  pi->stopAnimation();
554  solver.disconnect();
555 
556  stopB->setEnabled(false);
557  solveB->setEnabled(true);
558 
559  if (exist_status != 0 || access("/tmp/solution.wcs", F_OK)==-1)
560  {
561  appendLogText(i18n("Solver failed. Try again."));
562  azStage = AZ_INIT;
563  altStage = ALT_INIT;
564  return;
565  }
566 
567  connect(&wcsinfo, SIGNAL(finished(int)), this, SLOT(wcsinfoComplete(int)));
568 
569  wcsinfo.start("wcsinfo", QStringList("/tmp/solution.wcs"));
570 
571 }
572 
573 void Align::wcsinfoComplete(int exist_status)
574 {
575 
576  wcsinfo.disconnect();
577 
578  if (exist_status != 0)
579  {
580  appendLogText(i18n("WCS header missing or corrupted. Solver failed."));
581  azStage = AZ_INIT;
582  altStage = ALT_INIT;
583  return;
584  }
585 
586  QString wcsinfo_stdout = wcsinfo.readAllStandardOutput();
587 
588  QStringList wcskeys = wcsinfo_stdout.split(QRegExp("[\n]"));
589 
590  QStringList key_value;
591 
592  foreach(QString key, wcskeys)
593  {
594  key_value = key.split(" ");
595 
596  if (key_value.size() > 1)
597  {
598  if (key_value[0] == "ra_center")
599  alignCoord.setRA0(key_value[1].toDouble()/15.0);
600  else if (key_value[0] == "dec_center")
601  alignCoord.setDec0(key_value[1].toDouble());
602  else if (key_value[0] == "orientation_center")
603  RotOut->setText(key_value[1]);
604  }
605 
606  }
607 
608  // Convert to JNow
609  alignCoord.apparentCoord((long double) J2000, KStars::Instance()->data()->ut().djd());
610 
611  QString ra_dms, dec_dms;
612  getFormattedCoords(alignCoord.ra().Hours(), alignCoord.dec().Degrees(), ra_dms, dec_dms);
613 
614  SolverRAOut->setText(ra_dms);
615  SolverDecOut->setText(dec_dms);
616 
617  int elapsed = (int) round(solverTimer.elapsed()/1000.0);
618  appendLogText(i18np("Solver completed in %1 second.", "Solver completed in %1 seconds.", elapsed));
619 
620  executeMode();
621 
622  // Remove files left over by the solver
623  int fitsLoc = fitsFile.indexOf("fits");
624  QString tmpDir = fitsFile.left(fitsLoc);
625  QString tmpFITS = fitsFile.remove(tmpDir);
626 
627  tmpFITS.replace(".tmp", "*.*");
628 
629  QDir dir(tmpDir);
630  dir.setNameFilters(QStringList() << tmpFITS);
631  dir.setFilter(QDir::Files);
632  foreach(QString dirFile, dir.entryList())
633  dir.remove(dirFile);
634 
635 }
636 
637 void Align::logSolver()
638 {
639  qDebug() << solver.readAll() << endl;
640 }
641 
642 void Align::appendLogText(const QString &text)
643 {
644  logText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"), text));
645 
646  emit newLog();
647 }
648 
649 void Align::clearLog()
650 {
651  logText.clear();
652  emit newLog();
653 }
654 
655 void Align::updateScopeCoords(INumberVectorProperty *coord)
656 {
657  QString ra_dms, dec_dms;
658 
659  if (!strcmp(coord->name, "EQUATORIAL_EOD_COORD"))
660  {
661  getFormattedCoords(coord->np[0].value, coord->np[1].value, ra_dms, dec_dms);
662 
663  telescopeCoord.setRA(coord->np[0].value);
664  telescopeCoord.setDec(coord->np[1].value);
665 
666  ScopeRAOut->setText(ra_dms);
667  ScopeDecOut->setText(dec_dms);
668 
669  switch (azStage)
670  {
671  case AZ_SYNCING:
672  if (currentTelescope->isSlewing())
673  azStage=AZ_SLEWING;
674  break;
675 
676  case AZ_SLEWING:
677  if (currentTelescope->isSlewing() == false)
678  {
679  azStage = AZ_SECOND_TARGET;
680  measureAzError();
681  }
682  break;
683 
684  case AZ_CORRECTING:
685  if (currentTelescope->isSlewing() == false)
686  {
687  if(decDeviation > 0){
688  appendLogText(i18n("Slew complete. Please adjust your mount's' azimuth knob eastward until the target is in the center of the view."));
689  }
690  else{
691  appendLogText(i18n("Slew complete. Please adjust your mount's' azimuth knob westward until the target is in the center of the view."));
692  }
693  azStage = AZ_INIT;
694  }
695  break;
696 
697  default:
698  break;
699  }
700 
701  switch (altStage)
702  {
703  case ALT_SYNCING:
704  if (currentTelescope->isSlewing())
705  altStage = ALT_SLEWING;
706  break;
707 
708  case ALT_SLEWING:
709  if (currentTelescope->isSlewing() == false)
710  {
711  altStage = ALT_SECOND_TARGET;
712  measureAltError();
713  }
714  break;
715 
716  case ALT_CORRECTING:
717  if (currentTelescope->isSlewing() == false)
718  {
719  if(decDeviation > 0){
720  appendLogText(i18n("Slew complete. Please lower the altitude knob on your mount until the target is in the center of the view."));
721  }
722  else{
723  appendLogText(i18n("Slew complete. Please raise the altitude knob on your mount until the target is in the center of the view."));
724  }
725  altStage = ALT_INIT;
726  }
727  break;
728 
729 
730  default:
731  break;
732  }
733  }
734 
735  if (!strcmp(coord->name, "TELESCOPE_INFO"))
736  syncTelescopeInfo();
737 
738 }
739 
740 void Align::executeMode()
741 {
742  if (gotoR->isChecked())
743  executeGOTO();
744  else
745  executePolarAlign();
746 }
747 
748 
749 void Align::executeGOTO()
750 {
751  if (syncR->isChecked())
752  Sync();
753  else if (slewR->isChecked())
754  SlewToTarget();
755 }
756 
757 void Align::Sync()
758 {
759  if (currentTelescope->Sync(&alignCoord))
760  appendLogText(i18n("Syncing successful."));
761  else
762  appendLogText(i18n("Syncing failed."));
763 
764 }
765 
766 void Align::SlewToTarget()
767 {
768  if (canSync)
769  Sync();
770 
771  currentTelescope->Slew(&targetCoord);
772 
773  appendLogText(i18n("Slewing to target."));
774 }
775 
776 void Align::checkPolarAlignment()
777 {
778  if (polarR->isChecked())
779  {
780  measureAltB->setEnabled(true);
781  measureAzB->setEnabled(true);
782  gotoBox->setEnabled(false);
783  }
784  else
785  {
786  measureAltB->setEnabled(false);
787  measureAzB->setEnabled(false);
788  gotoBox->setEnabled(true);
789  }
790 }
791 
792 void Align::executePolarAlign()
793 {
794  appendLogText(i18n("Processing solution for polar alignment..."));
795 
796  switch (azStage)
797  {
798  case AZ_FIRST_TARGET:
799  case AZ_FINISHED:
800  measureAzError();
801  break;
802 
803  default:
804  break;
805  }
806 
807  switch (altStage)
808  {
809  case ALT_FIRST_TARGET:
810  case ALT_FINISHED:
811  measureAltError();
812  break;
813 
814  default:
815  break;
816  }
817 }
818 
819 
820 void Align::measureAzError()
821 {
822  static double initRA=0, initDEC=0, finalRA=0, finalDEC=0;
823 
824  switch (azStage)
825  {
826  case AZ_INIT:
827 
828  // Display message box confirming user point scope near meridian and south
829 
830  if (KMessageBox::warningContinueCancel( 0, hemisphereCombo->currentIndex() == 0
831  ? i18n("Point the telescope at the southern meridian. Press continue when ready.")
832  : i18n("Point the telescope at the northern meridian. Press continue when ready.")
833  , i18n("Polar Alignment Measurement"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
834  "ekos_measure_az_error")!=KMessageBox::Continue)
835  return;
836 
837  appendLogText(i18n("Solving first frame near the meridian."));
838  azStage = AZ_FIRST_TARGET;
839  polarR->setChecked(true);
840  solveB->click();
841  break;
842 
843  case AZ_FIRST_TARGET:
844  // start solving there, find RA/DEC
845  initRA = alignCoord.ra().Degrees();
846  initDEC = alignCoord.dec().Degrees();
847 
848  // Now move 30 arcminutes in RA
849  if (canSync)
850  {
851  azStage = AZ_SYNCING;
852  currentTelescope->Sync(initRA/15.0, initDEC);
853  currentTelescope->Slew((initRA - RAMotion)/15.0, initDEC);
854  }
855  // If telescope doesn't sync, we slew relative to its current coordinates
856  else
857  {
858  azStage = AZ_SLEWING;
859  currentTelescope->Slew(telescopeCoord.ra().Hours() - RAMotion/15.0, telescopeCoord.dec().Degrees());
860  }
861 
862  appendLogText(i18n("Slewing 30 arcminutes in RA..."));
863  break;
864 
865  case AZ_SECOND_TARGET:
866  // We reached second target now
867  // Let now solver for RA/DEC
868  appendLogText(i18n("Solving second frame near the meridian."));
869  azStage = AZ_FINISHED;
870  polarR->setChecked(true);
871  solveB->click();
872  break;
873 
874 
875  case AZ_FINISHED:
876  // Measure deviation in DEC
877  // Call function to report error
878  // set stage to AZ_FIRST_TARGET again
879  appendLogText(i18n("Calculating azimuth alignment error..."));
880  finalRA = alignCoord.ra().Degrees();
881  finalDEC = alignCoord.dec().Degrees();
882 
883  // Slew back to original position
884  if (canSync)
885  currentTelescope->Slew(initRA/15.0, initDEC);
886  else
887  {
888  currentTelescope->Slew(telescopeCoord.ra().Hours() + RAMotion/15.0, telescopeCoord.dec().Degrees());
889  }
890 
891  appendLogText(i18n("Slewing back to original position..."));
892 
893  calculatePolarError(initRA, initDEC, finalRA, finalDEC);
894 
895  azStage = AZ_INIT;
896  break;
897 
898  default:
899  break;
900 
901  }
902 
903 }
904 
905 void Align::measureAltError()
906 {
907  static double initRA=0, initDEC=0, finalRA=0, finalDEC=0;
908 
909  switch (altStage)
910  {
911  case ALT_INIT:
912 
913  // Display message box confirming user point scope near meridian and south
914 
915  if (KMessageBox::warningContinueCancel( 0, altDirCombo->currentIndex() == 0
916  ? i18n("Point the telescope to the east with a minimum altitude of 20 degrees. Press continue when ready.")
917  : i18n("Point the telescope to the west with a minimum altitude of 20 degrees. Press continue when ready.")
918  , i18n("Polar Alignment Measurement"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
919  "ekos_measure_alt_error")!=KMessageBox::Continue)
920  return;
921 
922  appendLogText(i18n("Solving first frame."));
923  altStage = ALT_FIRST_TARGET;
924  polarR->setChecked(true);
925  solveB->click();
926  break;
927 
928  case ALT_FIRST_TARGET:
929  // start solving there, find RA/DEC
930  initRA = alignCoord.ra().Degrees();
931  initDEC = alignCoord.dec().Degrees();
932 
933  // Now move 30 arcminutes in RA
934  if (canSync)
935  {
936  altStage = ALT_SYNCING;
937  currentTelescope->Sync(initRA/15.0, initDEC);
938  currentTelescope->Slew((initRA - RAMotion)/15.0, initDEC);
939 
940  }
941  // If telescope doesn't sync, we slew relative to its current coordinates
942  else
943  {
944  altStage = ALT_SLEWING;
945  currentTelescope->Slew(telescopeCoord.ra().Hours() - RAMotion/15.0, telescopeCoord.dec().Degrees());
946  }
947 
948 
949  appendLogText(i18n("Slewing 30 arcminutes in RA..."));
950  break;
951 
952  case ALT_SECOND_TARGET:
953  // We reached second target now
954  // Let now solver for RA/DEC
955  appendLogText(i18n("Solving second frame."));
956  altStage = ALT_FINISHED;
957  polarR->setChecked(true);
958  solveB->click();
959  break;
960 
961 
962  case ALT_FINISHED:
963  // Measure deviation in DEC
964  // Call function to report error
965  appendLogText(i18n("Calculating altitude alignment error..."));
966  finalRA = alignCoord.ra().Degrees();
967  finalDEC = alignCoord.dec().Degrees();
968 
969  // Slew back to original position
970  if (canSync)
971  currentTelescope->Slew(initRA/15.0, initDEC);
972  // If telescope doesn't sync, we slew relative to its current coordinates
973  else
974  {
975  currentTelescope->Slew(telescopeCoord.ra().Hours() + RAMotion/15.0, telescopeCoord.dec().Degrees());
976  }
977 
978  appendLogText(i18n("Slewing back to original position..."));
979 
980  calculatePolarError(initRA, initDEC, finalRA, finalDEC);
981 
982  altStage = ALT_INIT;
983  break;
984 
985  default:
986  break;
987 
988  }
989 
990 }
991 
992 void Align::calculatePolarError(double initRA, double initDEC, double finalRA, double finalDEC)
993 {
994  double raMotion = finalRA - initRA;
995  decDeviation = finalDEC - initDEC;
996 
997  // How much time passed siderrally form initRA to finalRA?
998  double RATime = fabs(raMotion / SIDRATE) / 60.0;
999 
1000  qDebug() << "initRA " << initRA << " initDEC " << initDEC << " finalRA " << finalRA << " finalDEC " << finalDEC << endl;
1001  qDebug() << "decDeviation " << decDeviation*3600 << " arcsec " << " RATime " << RATime << endl;
1002 
1003  // Equation by Frank Berret (Measuring Polar Axis Alignment Error, page 4)
1004  // In degrees
1005  double deviation = (3.81 * (decDeviation * 3600) ) / ( RATime * cos(initDEC * dms::DegToRad)) / 60.0;
1006 
1007  KLocalizedString deviationDirection;
1008 
1009  switch (hemisphereCombo->currentIndex())
1010  {
1011  // Northern hemisphere
1012  case 0:
1013  if (azStage == AZ_FINISHED)
1014  {
1015  if (decDeviation > 0)
1016  deviationDirection = ki18n("%1° too far west");
1017  else
1018  deviationDirection = ki18n("%1° too far east");
1019  }
1020  else if (altStage == ALT_FINISHED)
1021  {
1022  switch (altDirCombo->currentIndex())
1023  {
1024  // East
1025  case 0:
1026  if (decDeviation > 0)
1027  deviationDirection = ki18n("%1° too far high");
1028  else
1029  deviationDirection = ki18n("%1° too far low");
1030  break;
1031 
1032  // West
1033  case 1:
1034  if (decDeviation > 0)
1035  deviationDirection = ki18n("%1° too far low");
1036  else
1037  deviationDirection = ki18n("%1° too far high");
1038  break;
1039 
1040  default:
1041  break;
1042  }
1043  }
1044  break;
1045 
1046  // Southern hemisphere
1047  case 1:
1048  if (azStage == AZ_FINISHED)
1049  {
1050  if (decDeviation > 0)
1051  deviationDirection = ki18n("%1° too far east");
1052  else
1053  deviationDirection = ki18n("%1° too far west");
1054  }
1055  else if (altStage == ALT_FINISHED)
1056  {
1057  switch (altDirCombo->currentIndex())
1058  {
1059  // East
1060  case 0:
1061  if (decDeviation > 0)
1062  deviationDirection = ki18n("%1° too far low");
1063  else
1064  deviationDirection = ki18n("%1° too far high");
1065  break;
1066 
1067  // West
1068  case 1:
1069  if (decDeviation > 0)
1070  deviationDirection = ki18n("%1° too far high");
1071  else
1072  deviationDirection = ki18n("%1° too far low");
1073  break;
1074 
1075  default:
1076  break;
1077  }
1078  }
1079  break;
1080 
1081  default:
1082  break;
1083 
1084  }
1085 
1086  if (azStage == AZ_FINISHED)
1087  {
1088  azError->setText(deviationDirection.subs(QString("%1").arg(fabs(deviation), 0, 'g', 3)).toString());
1089  azDeviation = deviation * (decDeviation > 0 ? 1 : -1);
1090  correctAzB->setEnabled(true);
1091  }
1092  if (altStage == ALT_FINISHED)
1093  {
1094  altError->setText(deviationDirection.subs(QString("%1").arg(fabs(deviation), 0, 'g', 3)).toString());
1095  altDeviation = deviation * (decDeviation > 0 ? 1 : -1);
1096  correctAltB->setEnabled(true);
1097  }
1098 }
1099 
1100 void Align::correctAltError()
1101 {
1102  double newRA, newDEC, currentAlt, currentAz;
1103 
1104  SkyPoint currentCoord (telescopeCoord);
1105  dms targetLat;
1106 
1107  targetLat.setD(KStars::Instance()->data()->geo()->lat()->Degrees() - altDeviation);
1108 
1109  currentCoord.EquatorialToHorizontal(KStars::Instance()->data()->lst(), &targetLat );
1110 
1111  currentAlt = currentCoord.alt().Degrees();
1112  currentAz = currentCoord.az().Degrees();
1113 
1114  currentCoord.setAlt(currentAlt);
1115  currentCoord.setAz(currentAz);
1116 
1117  currentCoord.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat());
1118 
1119  newRA = currentCoord.ra().Hours();
1120  newDEC = currentCoord.dec().Degrees();
1121 
1122  altStage = ALT_CORRECTING;
1123 
1124  currentTelescope->Slew(newRA, newDEC);
1125 
1126  appendLogText(i18n("Slewing to calibration position, please wait until telescope is finished slewing."));
1127 
1128 }
1129 
1130 void Align::correctAzError()
1131 {
1132  double newRA, newDEC, currentAlt, currentAz;
1133 
1134  SkyPoint currentCoord (telescopeCoord);
1135 
1136  currentCoord.EquatorialToHorizontal(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat());
1137 
1138  currentAlt = currentCoord.alt().Degrees();
1139  currentAz = currentCoord.az().Degrees() + azDeviation;
1140 
1141  currentCoord.setAlt(currentAlt);
1142  currentCoord.setAz(currentAz);
1143 
1144  currentCoord.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat());
1145 
1146  newRA = currentCoord.ra().Hours();
1147  newDEC = currentCoord.dec().Degrees();
1148 
1149  azStage = AZ_CORRECTING;
1150 
1151  currentTelescope->Slew(newRA, newDEC);
1152 
1153  appendLogText(i18n("Slewing to calibration position, please wait until telescope is finished slewing."));
1154 
1155 }
1156 
1157 void Align::getFormattedCoords(double ra, double dec, QString &ra_str, QString &dec_str)
1158 {
1159  dms ra_s,dec_s;
1160  ra_s.setH(ra);
1161  dec_s.setD(dec);
1162 
1163  ra_str = QString("%1:%2:%3").arg(ra_s.hour(), 2, 10, QChar('0')).arg(ra_s.minute(), 2, 10, QChar('0')).arg(ra_s.second(), 2, 10, QChar('0'));
1164  if (dec_s.Degrees() < 0)
1165  dec_str = QString("-%1:%2:%3").arg(abs(dec_s.degree()), 2, 10, QChar('0')).arg(abs(dec_s.arcmin()), 2, 10, QChar('0')).arg(dec_s.arcsec(), 2, 10, QChar('0'));
1166  else
1167  dec_str = QString("%1:%2:%3").arg(dec_s.degree(), 2, 10, QChar('0')).arg(dec_s.arcmin(), 2, 10, QChar('0')).arg(dec_s.arcsec(), 2, 10, QChar('0'));
1168 }
1169 
1170 }
1171 
1172 #include "align.moc"
dms::hour
int hour() const
Definition: dms.h:104
Ekos::Align::checkCCD
void checkCCD(int CCDNum)
Definition: align.cpp:109
Ekos::Align::AZ_SYNCING
Definition: align.h:36
Ekos::Align::measureAzError
void measureAzError()
Definition: align.cpp:820
fitstab.h
SkyPoint::ra
const dms & ra() const
Definition: skypoint.h:171
Options::astrometryConfFile
static QString astrometryConfFile()
Get astrometry.net configuration file.
Definition: Options.h:5015
Ekos::Align::AZ_SECOND_TARGET
Definition: align.h:36
ISD::CCD::getChip
CCDChip * getChip(CCDChip::ChipType cType)
Definition: indiccd.cpp:872
Ekos::Align::ALT_SLEWING
Definition: align.h:37
SkyPoint::apparentCoord
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
Definition: skypoint.cpp:433
ISD::CCDChip
Definition: indiccd.h:23
SkyPoint::az
const dms & az() const
Definition: skypoint.h:177
dms::minute
int minute() const
Definition: dms.cpp:174
ISD::CCDChip::isCapturing
bool isCapturing()
Definition: indiccd.cpp:255
align.h
Ekos::Align::syncCCDInfo
void syncCCDInfo()
Definition: align.cpp:194
ISD::Telescope::isSlewing
bool isSlewing()
Definition: inditelescope.cpp:115
SkyPoint::setAz
void setAz(dms az)
Sets Az, the Azimuth.
Definition: skypoint.h:152
dms::Degrees
const double & Degrees() const
Definition: dms.h:98
ISD::GDInterface::getDeviceName
virtual const char * getDeviceName()=0
Ekos::Align::setCCD
void setCCD(ISD::GDInterface *newCCD)
Definition: align.cpp:118
dms::degree
int degree() const
Definition: dms.h:79
Ekos::Align::ALT_CORRECTING
Definition: align.h:37
ISD::Telescope::canSync
bool canSync()
Definition: inditelescope.cpp:93
KStars::Instance
static KStars * Instance()
Definition: kstars.h:125
Ekos::Align::startSovling
void startSovling(const QString &filename)
Definition: align.cpp:497
QProgressIndicator.h
Ekos::Align::stopSolving
void stopSolving()
Definition: align.cpp:525
Ekos::Align::correctAltError
void correctAltError()
Definition: align.cpp:1100
dms::second
int second() const
Definition: dms.cpp:182
fitsview.h
QProgressIndicator::startAnimation
void startAnimation()
Definition: QProgressIndicator.cpp:55
ISD::CCDChip::PRIMARY_CCD
Definition: indiccd.h:26
Options::alignExposure
static double alignExposure()
Get Default alignment exposure value.
Definition: Options.h:4749
ISD::CCDChip::abortExposure
bool abortExposure()
Definition: indiccd.cpp:224
Ekos::Align::ALT_SYNCING
Definition: align.h:37
QProgressIndicator
The QProgressIndicator class lets an application display a progress indicator to show that a lengthy ...
Definition: QProgressIndicator.h:35
dms.h
Ekos::Align::AZ_SLEWING
Definition: align.h:36
driverinfo.h
Ekos::Align::copyCoordsToBoxes
void copyCoordsToBoxes()
Definition: align.cpp:432
ISD::CCDChip::capture
bool capture(double exposure)
Definition: indiccd.cpp:198
Ekos::Align::correctAzError
void correctAzError()
Definition: align.cpp:1130
SkyPoint
The sky coordinates of a point in the sky.
Definition: skypoint.h:50
FITS_WCSM
Definition: fitscommon.h:20
ISD::CCDChip::GUIDE_CCD
Definition: indiccd.h:26
ISD::CCDChip::setFrameType
bool setFrameType(CCDFrameType fType)
Definition: indiccd.cpp:299
Ekos::Align::AZ_FIRST_TARGET
Definition: align.h:36
Ekos::Align::newLog
void newLog()
ISD::Telescope::Sync
bool Sync(SkyPoint *ScopeTarget)
Definition: inditelescope.cpp:356
ISD::Telescope
Definition: inditelescope.h:19
i18nc
i18nc("string from libindi, used in the config dialog","100x")
SkyPoint::HorizontalToEquatorial
void HorizontalToEquatorial(const dms *LST, const dms *lat)
Determine the (RA, Dec) coordinates of the SkyPoint from its (Altitude, Azimuth) coordinates, given the local sidereal time and the observer's latitude.
Definition: skypoint.cpp:102
Ekos::Align::AZ_INIT
Definition: align.h:36
Ekos::Align::setTelescope
void setTelescope(ISD::GDInterface *newTelescope)
Definition: align.cpp:129
dms
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:42
Ekos::Align::ALT_INIT
Definition: align.h:37
Ekos::Align::generateArgs
void generateArgs()
Definition: align.cpp:354
ISD::CCDChip::getFrame
bool getFrame(int *x, int *y, int *w, int *h)
Definition: indiccd.cpp:106
Ekos::Align::checkPolarAlignment
void checkPolarAlignment()
Definition: align.cpp:776
SkyPoint::dec
const dms & dec() const
Definition: skypoint.h:174
dms::Hours
double Hours() const
Definition: dms.h:125
QProgressIndicator::stopAnimation
void stopAnimation()
Definition: QProgressIndicator.cpp:63
Ekos::Align::Align
Align()
Definition: align.cpp:40
Ekos::Align::wcsinfoComplete
void wcsinfoComplete(int exist_status)
Definition: align.cpp:573
SkyPoint::EquatorialToHorizontal
void EquatorialToHorizontal(const dms *LST, const dms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates, given the local sidereal time and the observer's latitude.
Definition: skypoint.cpp:55
Ekos::Align::clearCoordBoxes
void clearCoordBoxes()
Definition: align.cpp:440
Options.h
Ekos::Align::newFITS
void newFITS(IBLOB *bp)
Definition: align.cpp:484
QTextStream
Ekos::Align::updateScopeCoords
void updateScopeCoords(INumberVectorProperty *coord)
Definition: align.cpp:655
Ekos::Align::syncTelescopeInfo
void syncTelescopeInfo()
Definition: align.cpp:138
SkyPoint::setRA0
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
Definition: skypoint.h:97
ISD::Telescope::Slew
bool Slew(SkyPoint *ScopeTarget)
Definition: inditelescope.cpp:322
Ekos::Align::AZ_CORRECTING
Definition: align.h:36
J2000
#define J2000
Definition: kstarsdatetime.h:21
Ekos::Align::ALT_FIRST_TARGET
Definition: align.h:37
Ekos::Align::ALT_FINISHED
Definition: align.h:37
Ekos::Align::~Align
~Align()
Definition: align.cpp:103
Options::setAlignExposure
static void setAlignExposure(double v)
Set Default alignment exposure value.
Definition: Options.h:4739
Ekos::Align::measureAltError
void measureAltError()
Definition: align.cpp:905
Ekos::Align::appendLogText
void appendLogText(const QString &)
Definition: align.cpp:642
CCDFrameType
CCDFrameType
Definition: indicommon.h:68
Ekos::Align::checkLineEdits
void checkLineEdits()
Definition: align.cpp:423
ISD::CCDChip::setCaptureMode
void setCaptureMode(FITSMode mode)
Definition: indiccd.h:32
ISD::DeviceDecorator::isConnected
virtual bool isConnected()
Definition: indistd.cpp:718
Ekos::Align::logSolver
void logSolver()
Definition: align.cpp:637
dms::arcsec
int arcsec() const
Definition: dms.cpp:156
ISD::DeviceDecorator::getBaseDevice
virtual INDI::BaseDevice * getBaseDevice()
Definition: indistd.cpp:708
Ekos::Align::capture
bool capture()
Definition: align.cpp:448
SkyPoint::setAlt
void setAlt(dms alt)
Sets Alt, the Altitude.
Definition: skypoint.h:141
SkyPoint::setRA
void setRA(dms r)
Sets RA, the current Right Ascension.
Definition: skypoint.h:119
fitsviewer.h
SkyPoint::setDec
void setDec(dms d)
Sets Dec, the current Declination.
Definition: skypoint.h:130
kstarsdata.h
SkyPoint::setDec0
void setDec0(dms d)
Sets Dec0, the catalog Declination.
Definition: skypoint.h:108
Ekos::Align::clearLog
void clearLog()
Definition: align.cpp:649
SkyPoint::alt
const dms & alt() const
Definition: skypoint.h:180
dms::setD
void setD(const double &x)
Sets floating-point value of angle, in degrees.
Definition: dms.h:130
indicommon.h
ISD::Telescope::getEqCoords
bool getEqCoords(double *ra, double *dec)
Definition: inditelescope.cpp:410
Ekos::Align::solverComplete
void solverComplete(int exist_status)
Definition: align.cpp:551
kstars.h
ISD::GDInterface
Definition: indistd.h:48
Ekos::Align::AZ_FINISHED
Definition: align.h:36
FRAME_LIGHT
Definition: indicommon.h:68
dms::arcmin
int arcmin() const
Definition: dms.cpp:148
Ekos::Align::ALT_SECOND_TARGET
Definition: align.h:37
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:36:19 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kstars

Skip menu "kstars"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdeedu API Reference

Skip menu "kdeedu API Reference"
  • Analitza
  •     lib
  • kalgebra
  • kalzium
  •   libscience
  • kanagram
  • kig
  •   lib
  • klettres
  • kstars
  • libkdeedu
  •   keduvocdocument
  • marble
  • parley
  • rocs
  •   App
  •   RocsCore
  •   VisualEditor
  •   stepcore

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal