diff --git a/mainwindow.cpp b/mainwindow.cpp index 5308262..c24a0bc 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -12,6 +12,7 @@ #include #include #include +#include "photocapture.h" #ifdef __linux__ #include @@ -35,6 +36,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), setCentralWidget(m_image); resize(800, 600); + PhotoCapture *photoCapture = new PhotoCapture(this); + QDockWidget *captureDock = new QDockWidget(tr("Photo capture"), this); + captureDock->setWidget(photoCapture); + addDockWidget(Qt::BottomDockWidgetArea, captureDock); + setWindowTitle(tr("Tenmon")); m_ringList = new ImageRingList(this); diff --git a/phd.cpp b/phd.cpp new file mode 100644 index 0000000..d28ffe2 --- /dev/null +++ b/phd.cpp @@ -0,0 +1,93 @@ +#include "phd.h" +#include +#include +#include + +const QHash stateMap = {{Stopped, "Stopped"}, {Selected,"Selected"}, {Calibrating,"Calibrating"}, {Guiding,"Guiding"}, + {LostLock,"LostLock"}, {Paused,"Paused"}, {Looping,"Looping"}}; + +PHD::PHD(QObject *parent) : QObject(parent) + ,_messageId(0) +{ + _socket = new QTcpSocket(this); + connect(_socket, SIGNAL(readyRead()), this, SLOT(readyRead())); +} + +void PHD::connectTo(const QHostAddress &address, int port) +{ + if(_socket->state() == QTcpSocket::ConnectedState) + { + _socket->disconnect(); + _socket->waitForDisconnected(); + } + _socket->connectToHost(address, port); +} + +QString PHD::stateToString(PHDState state) +{ + if(stateMap.contains(state)) + return stateMap[state]; + else + return QString(); +} + +PHDState PHD::stringToState(const QString &state) +{ + return stateMap.key(state, Unknown); +} + +void PHD::dither(float pixels, bool raOnly, int time, int timeout) +{ + QVariantList params; + params.append(pixels); + params.append(raOnly); + params.append(QVariantHash({{"pixels",pixels/2.0},{"time",time},{"timeout",timeout}})); + invokeMethod("dither", params); +} + +void PHD::parseMessage() +{ + QJsonDocument json = QJsonDocument::fromJson(_data); + QJsonObject message = json.object(); + //qDebug() << message; + if(message.contains("Event")) + { + QString event = message.value("Event").toString(); + if(event=="AppState") + emit stateChanged(stringToState(message.value("State").toString())); + else if(event=="SettleDone") + { + qDebug() << message; + emit settled(); + } + } + + _data.clear(); +} + +void PHD::invokeMethod(const QString &method, const QVariantList ¶ms) +{ + QJsonDocument json; + QJsonObject message; + message.insert("method", method); + message.insert("id", QString::number(_messageId++)); + message.insert("params", QJsonArray::fromVariantList(params)); + json.setObject(message); + QByteArray data = json.toJson(QJsonDocument::Compact); + data.append('\n'); + _socket->write(data); +} + +void PHD::readyRead() +{ + QByteArray data = _socket->readAll(); + int index; + while((index = data.indexOf('\n')) != -1) + { + _data.append(data.left(index)); + data.remove(0, index+1); + parseMessage(); + } + if(data.size()) + _data.append(data); +} diff --git a/phd.h b/phd.h new file mode 100644 index 0000000..b5bfa1f --- /dev/null +++ b/phd.h @@ -0,0 +1,42 @@ +#ifndef PHD_H +#define PHD_H + +#include +#include + +typedef enum +{ + Stopped, + Selected, + Calibrating, + Guiding, + LostLock, + Paused, + Looping, + Unknown, +}PHDState; + +class PHD : public QObject +{ + Q_OBJECT + QTcpSocket *_socket; + QByteArray _data; + int _messageId; +public: + explicit PHD(QObject *parent = nullptr); + void connectTo(const QHostAddress &address, int port = 4400); + QString stateToString(PHDState state); + PHDState stringToState(const QString &state); + void dither(float pixels, bool raOnly, int time = 5, int timeout = 30); +private: + void parseMessage(); + void invokeMethod(const QString &method, const QVariantList ¶ms); +private slots: + void readyRead(); +signals: + void connected(); + void settled(); + void stateChanged(PHDState state); +}; + +#endif // PHD_H diff --git a/photocapture.cpp b/photocapture.cpp new file mode 100644 index 0000000..ff7c8f9 --- /dev/null +++ b/photocapture.cpp @@ -0,0 +1,142 @@ +#include "photocapture.h" +#include +#include +#include +#include +#include +#include +#include +#include + +PhotoCapture::PhotoCapture(QWidget *parent) : QWidget(parent) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + setLayout(layout); + + layout->addWidget(new QLabel(tr("Exposure time"), this)); + _exposureTime = new QSpinBox(this); + _exposureTime->setValue(60); + _exposureTime->setSingleStep(60); + _exposureTime->setRange(0, 3600); + _exposureTime->setSuffix("s"); + layout->addWidget(_exposureTime); + + layout->addWidget(new QLabel(tr("Count"), this)); + _numberOfExposures = new QSpinBox(this); + _numberOfExposures->setValue(1); + layout->addWidget(_numberOfExposures); + + layout->addWidget(new QLabel(tr("Dither frequency"), this)); + _ditherFreq = new QSpinBox(this); + _ditherFreq->setValue(1); + _ditherFreq->setRange(0, 10); + layout->addWidget(_ditherFreq); + + _startCapture = new QPushButton(tr("Start capture"), this); + connect(_startCapture, SIGNAL(clicked()), this, SLOT(startCaputre())); + layout->addWidget(_startCapture); + + QPushButton *selectDirButton = new QPushButton(tr("Select output"), this); + layout->addWidget(selectDirButton); + connect(selectDirButton, SIGNAL(clicked()), this, SLOT(selectDir())); + + _process = new QProcess(this); + _process->setProgram("gphoto2"); + connect(_process, SIGNAL(finished(int)), this, SLOT(processFinished(int))); + connect(_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readStdout())); + connect(_process, SIGNAL(readyReadStandardError()), this, SLOT(readStderr())); + + QStringList paths = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); + if(paths.size()) + _outputDir.setPath(paths.first()+"/astro"); + + _timer = new QTimer(this); + _timer->setInterval(1000); + _timer->setSingleShot(true); + connect(_timer, SIGNAL(timeout()), this, SLOT(startCaputre())); + + _phd = new PHD(this); + _phd->connectTo(QHostAddress("127.0.0.1")); + connect(_phd, SIGNAL(settled()), this, SLOT(startCaputre())); + + _photoCount = 0; +} + +PhotoCapture::~PhotoCapture() +{ + if(_process->state()==QProcess::Running) + { + _process->terminate(); + _process->waitForFinished(5000); + } +} + +void PhotoCapture::startCaputre() +{ + if(_process->state()==QProcess::Running) + { + _process->terminate(); + _process->waitForFinished(10000); + _startCapture->setText(tr("Start capture")); + _numberOfExposures->setValue(0); + _photoCount = 0; + } + else if(_numberOfExposures->value()>0) + { + _photoCount++; + _startCapture->setText(tr("Stop capture")); + QString file = "capt"+QDateTime::currentDateTime().toString(Qt::ISODate)+".cr2"; + QString path = _outputDir.absoluteFilePath(file); + + QStringList args = {"--force-overwrite", "--set-config", "eosremoterelease=5", + QString("--wait-event=%1s").arg(_exposureTime->value()), + "--set-config", "eosremoterelease=9", + "--wait-event-and-download=3s", + QString("--filename=%1").arg(path)}; + _process->setArguments(args); + _process->start(); + } + else + { + _photoCount = 0; + _startCapture->setText(tr("Start capture")); + } +} + +void PhotoCapture::processFinished(int exit) +{ + if(exit==0) + { + _numberOfExposures->setValue(_numberOfExposures->value()-1); + if(_ditherFreq->value() == _photoCount) + { + _photoCount = 0; + _phd->dither(4, false); + } + else + { + _timer->start(); + } + } + else + { + qDebug() << "gphoto failed" << exit; + _startCapture->setText(tr("Start capture")); + } +} + +void PhotoCapture::selectDir() +{ + QString path = QFileDialog::getExistingDirectory(this, tr("Output directory"), _outputDir.absolutePath()); + _outputDir.setPath(path); +} + +void PhotoCapture::readStdout() +{ + //qDebug() << _process->readAllStandardOutput(); +} + +void PhotoCapture::readStderr() +{ + //qDebug() << _process->readAllStandardError(); +} diff --git a/photocapture.h b/photocapture.h new file mode 100644 index 0000000..fb8f427 --- /dev/null +++ b/photocapture.h @@ -0,0 +1,37 @@ +#ifndef PHOTOCAPTURE_H +#define PHOTOCAPTURE_H + +#include +#include +#include +#include +#include +#include "phd.h" + +class PhotoCapture : public QWidget +{ + Q_OBJECT + QSpinBox *_exposureTime; + QSpinBox *_numberOfExposures; + QSpinBox *_ditherFreq; + QPushButton *_startCapture; + QProcess *_process; + QDir _outputDir; + QTimer *_timer; + PHD *_phd; + int _photoCount; +public: + explicit PhotoCapture(QWidget *parent = nullptr); + ~PhotoCapture(); +signals: + +public slots: + void startCaputre(); +private slots: + void processFinished(int exit); + void selectDir(); + void readStdout(); + void readStderr(); +}; + +#endif // PHOTOCAPTURE_H diff --git a/tenmon.pro b/tenmon.pro index c2b28a7..6ae294e 100644 --- a/tenmon.pro +++ b/tenmon.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui sql +QT += core gui sql network greaterThan(QT_MAJOR_VERSION, 4): QT += widgets @@ -27,7 +27,9 @@ SOURCES += main.cpp\ database.cpp \ loadrunable.cpp \ imageinfo.cpp \ - starfit.cpp + starfit.cpp \ + phd.cpp \ + photocapture.cpp HEADERS += mainwindow.h \ imagescrollarea.h \ @@ -36,4 +38,6 @@ HEADERS += mainwindow.h \ loadrunable.h \ imageinfo.h \ rawimage.h \ - starfit.h + starfit.h \ + phd.h \ + photocapture.h