11#include "skymapcomposite.h"
12#include "fitsviewer/fitsview.h"
13#include "fitsviewer/fitsdata.h"
14#include "indi/indilistener.h"
15#include "hips/hipsfinder.h"
16#include "kstarsdata.h"
17#include "ekos/auxiliary/darklibrary.h"
18#include "ekos/guide/guide.h"
19#include "ekos/align/align.h"
20#include "ekos/capture/capture.h"
21#include "ekos/capture/cameraprocess.h"
22#include "ekos/focus/focusmodule.h"
26#include "ekos_debug.h"
30#include <QtConcurrent>
32#include <QImageWriter>
41 m_Manager(manager), m_NodeManagers(nodeManagers)
43 for (
auto &nodeManager : m_NodeManagers)
45 connect(nodeManager->media(), &Node::connected,
this, &Media::onConnected);
46 connect(nodeManager->media(), &Node::disconnected,
this, &Media::onDisconnected);
47 connect(nodeManager->media(), &Node::onTextReceived,
this, &Media::onTextReceived);
48 connect(nodeManager->media(), &Node::onBinaryReceived,
this, &Media::onBinaryReceived);
51 connect(
this, &Media::newMetadata,
this, &Media::uploadMetadata);
61bool Media::isConnected()
const
63 return std::any_of(m_NodeManagers.begin(), m_NodeManagers.end(), [](
auto & nodeManager)
65 return nodeManager->media()->isConnected();
72void Media::onConnected()
74 auto node = qobject_cast<Node*>(sender());
78 qCInfo(KSTARS_EKOS) <<
"Connected to Media Websocket server at" << node->url().toDisplayString();
86void Media::onDisconnected()
88 auto node = qobject_cast<Node*>(sender());
92 qCInfo(KSTARS_EKOS) <<
"Disconnected from Message Websocket server at" << node->url().toDisplayString();
94 if (isConnected() ==
false)
98 for (
const QString &oneFile : temporaryFiles)
100 temporaryFiles.clear();
109void Media::onTextReceived(
const QString &message)
111 qCInfo(KSTARS_EKOS) <<
"Media Text Websocket Message" << message;
116 qCWarning(KSTARS_EKOS) <<
"Ekos Live Parsing Error" <<
error.errorString();
121 const QString command = msgObj[
"type"].toString();
122 const QJsonObject payload = msgObj[
"payload"].toObject();
124 if (command == commands[ALIGN_SET_FILE_EXTENSION])
125 extension = payload[
"ext"].toString();
126 else if (command == commands[SET_BLOBS])
127 m_sendBlobs = msgObj[
"payload"].toBool();
129 else if (command == commands[ASTRO_GET_OBJECTS_IMAGE])
131 int level = payload[
"level"].toInt(5);
132 double zoom = payload[
"zoom"].toInt(20000);
133 bool exact = payload[
"exact"].toBool(
false);
136 QVariantList objectNames = payload[
"names"].toArray().toVariantList();
138 for (
auto &oneName : objectNames)
145 double fov_w = 0, fov_h = 0;
147 if (oneObject->
type() == SkyObject::MOON || oneObject->
type() == SkyObject::PLANET)
152 <<
"--num_times" <<
"1"
153 <<
"--geometry" <<
QString(
"%1x%2").arg(HIPS_TILE_WIDTH).arg(HIPS_TILE_HEIGHT)
155 <<
"--output" << output);
157 centerImage.load(output);
160 HIPSFinder::Instance()->render(oneObject, level, zoom, ¢erImage, fov_w, fov_h);
162 if (!centerImage.isNull())
172 {
"name", exact ?
name : oneObject->
name()},
174 {
"resolution",
QString(
"%1x%2").
arg(HIPS_TILE_WIDTH).
arg(HIPS_TILE_HEIGHT)},
190 centerImage.save(&buffer,
"jpg", 90);
193 emit newImage(jpegData);
198 else if (command == commands[ASTRO_GET_SKYPOINT_IMAGE])
200 int level = payload[
"level"].toInt(5);
201 double zoom = payload[
"zoom"].toInt(20000);
202 double ra = payload[
"ra"].toDouble(0);
203 double de = payload[
"de"].toDouble(0);
204 double width = payload[
"width"].toDouble(512);
205 double height = payload[
"height"].toDouble(512);
209 SkyPoint J2000Coord(coords.ra(), coords.dec());
211 coords.setRA0(J2000Coord.ra());
212 coords.setDec0(J2000Coord.dec());
213 coords.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->
geo()->lat());
215 volatile auto jnowRAString = coords.ra().toHMSString();
216 volatile auto jnowDEString = coords.dec().toDMSString();
217 volatile auto j2000RAString = coords.ra0().toHMSString();
218 volatile auto j2000DEString = coords.dec0().toDMSString();
221 double fov_w = 0, fov_h = 0;
222 HIPSFinder::Instance()->render(&coords, level, zoom, ¢erImage, fov_w, fov_h);
224 if (!centerImage.isNull())
231 {
"uuid",
"skypoint_hips"},
232 {
"name",
"skypoint_hips"},
250 centerImage.save(&buffer,
"jpg", 95);
253 emit newImage(jpegData);
261void Media::onBinaryReceived(
const QByteArray &message)
267 QString metadataString = message.
left(METADATA_PACKET);
280 sendData(data,
"+D");
288 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false || isConnected() ==
false)
291 StretchParams params;
293 stretch(data, image, params);
294 upload(data, image, params, uuid);
302 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false || isConnected() ==
false)
306 data->loadFromFile(filename);
307#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
319 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false || isConnected() ==
false)
331 previewImage->loadData(data);
332 upload(previewImage, uuid);
340 double min = 0, max = 0;
341 data->getMinMax(&min, &max);
342 auto width = data->width();
343 auto height = data->height();
344 auto channels = data->channels();
345 auto dataType = data->dataType();
357 for (
int i = 0; i < 256; i++)
365 Stretch stretch(width, height, channels, dataType);
368 params = stretch.computeParams(data->getImageBuffer());
369 stretch.setParams(params);
370 stretch.run(data->getImageBuffer(), &image, 1);
386 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0);
387 imageData->getRecordValue(
"XBINNING", xbin);
388 imageData->getRecordValue(
"YBINNING", ybin);
389 imageData->getRecordValue(
"EXPTIME", exposure);
390 imageData->getRecordValue(
"GAIN", gain);
391 imageData->getRecordValue(
"PIXSIZE1", pixel_size);
392 imageData->getRecordValue(
"FOCALLEN", focal_length);
393 imageData->getRecordValue(
"APTDIA", aperture);
395 auto stretchParameters = view->getStretchParams();
398 const double binned_pixel = pixel_size.toDouble() * xbin.toInt();
403 {
"resolution", resolution},
405 {
"channels", imageData->channels()},
406 {
"mean", imageData->getAverageMean()},
407 {
"median", imageData->getAverageMedian()},
408 {
"stddev", imageData->getAverageStdDev()},
409 {
"min", imageData->getMin()},
410 {
"max", imageData->getMax()},
411 {
"bin",
QString(
"%1x%2").
arg(xbin.toString(), ybin.toString())},
414 {
"exposure", exposure.toString()},
415 {
"focal_length", focal_length.toString()},
416 {
"aperture", aperture.toString()},
417 {
"gain", gain.toString()},
419 {
"shadows", stretchParameters.grey_red.shadows},
420 {
"midtones", stretchParameters.grey_red.midtones},
421 {
"highlights", stretchParameters.grey_red.highlights},
422 {
"hasWCS", imageData->hasWCS()},
423 {
"hfr", imageData->getHFR()},
424 {
"view", view->objectName()},
435 auto fastImage = (!Options::ekosLiveHighBandwidth() || uuid[0] ==
'+');
436 auto scaleWidth = fastImage ? HB_IMAGE_WIDTH / 2 : HB_IMAGE_WIDTH;
440 QPixmap scaledImage = view->getDisplayPixmap().width() > scaleWidth ?
442 view->getDisplayPixmap();
447 emit newImage(jpegData);
463 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0);
464 data->getRecordValue(
"XBINNING", xbin);
465 data->getRecordValue(
"YBINNING", ybin);
466 data->getRecordValue(
"EXPTIME", exposure);
467 data->getRecordValue(
"GAIN", gain);
468 data->getRecordValue(
"PIXSIZE1", pixel_size);
469 data->getRecordValue(
"FOCALLEN", focal_length);
470 data->getRecordValue(
"APTDIA", aperture);
473 const double binned_pixel = pixel_size.toDouble() * xbin.toInt();
478 {
"resolution", resolution},
480 {
"channels", data->channels()},
481 {
"mean", data->getAverageMean()},
482 {
"median", data->getAverageMedian()},
483 {
"stddev", data->getAverageStdDev()},
484 {
"min", data->getMin()},
485 {
"max", data->getMax()},
486 {
"bin",
QString(
"%1x%2").
arg(xbin.toString(), ybin.toString())},
489 {
"exposure", exposure.toString()},
490 {
"focal_length", focal_length.toString()},
491 {
"aperture", aperture.toString()},
492 {
"gain", gain.toString()},
494 {
"shadows", params.grey_red.shadows},
495 {
"midtones", params.grey_red.midtones},
496 {
"highlights", params.grey_red.highlights},
497 {
"hasWCS", data->hasWCS()},
498 {
"hfr", data->getHFR()},
509 auto fastImage = (!Options::ekosLiveHighBandwidth() || uuid[0] ==
'+');
510 auto scaleWidth = fastImage ? HB_IMAGE_WIDTH / 2 : HB_IMAGE_WIDTH;
521 emit newImage(jpegData);
529 if (isConnected() ==
false)
542 const int32_t width = imageData->width();
543 const int32_t height = imageData->height();
546 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0);
547 imageData->getRecordValue(
"XBINNING", xbin);
548 imageData->getRecordValue(
"YBINNING", ybin);
549 imageData->getRecordValue(
"EXPTIME", exposure);
550 imageData->getRecordValue(
"GAIN", gain);
551 imageData->getRecordValue(
"PIXSIZE1", pixel_size);
552 imageData->getRecordValue(
"FOCALLEN", focal_length);
553 imageData->getRecordValue(
"APTDIA", aperture);
556 const double binned_pixel = pixel_size.toDouble() * xbin.toInt();
560 {
"resolution", resolution},
562 {
"channels", imageData->channels()},
563 {
"mean", imageData->getAverageMean()},
564 {
"median", imageData->getAverageMedian()},
565 {
"stddev", imageData->getAverageStdDev()},
566 {
"bin",
QString(
"%1x%2").
arg(xbin.toString()).
arg(ybin.toString())},
569 {
"exposure", exposure.toString()},
570 {
"focal_length", focal_length.toString()},
571 {
"aperture", aperture.toString()},
572 {
"gain", gain.toString()},
587 if (correctionVector.isNull() ==
false)
589 scaledImage = view->getDisplayPixmap();
590 const double currentZoom = view->getCurrentZoom();
591 const double normalizedZoom = currentZoom / 100;
593 if (fabs(normalizedZoom - 1) > 0.001)
594 scaledImage = view->getDisplayPixmap().scaledToWidth(view->zoomedWidth());
596 scaledImage = view->getDisplayPixmap();
598 QPointF center = 0.5 * correctionVector.p1() * normalizedZoom + 0.5 * correctionVector.p2() * normalizedZoom;
599 uint32_t length = qMax(correctionVector.length() / normalizedZoom, 100 / normalizedZoom);
601 QRect boundingRectable;
602 boundingRectable.
setSize(
QSize(length * 2, length * 2));
604 boundingRectable.
moveTo(topLeft);
607 emit newBoundingRect(boundingRectable, scaledImage.
size(), currentZoom);
609 scaledImage = scaledImage.
copy(boundingRectable);
613 scaledImage = view->getDisplayPixmap().width() > HB_IMAGE_WIDTH / 2 ?
615 view->getDisplayPixmap();
621 emit newImage(jpegData);
629 if (isConnected() ==
false ||
630 Options::ekosLiveImageTransfer() ==
false ||
631 m_sendBlobs ==
false ||
635 int32_t width = Options::ekosLiveHighBandwidth() ? HB_VIDEO_WIDTH : HB_VIDEO_WIDTH / 2;
640 QImage videoImage = (frame->width() > width) ? frame->scaledToWidth(width) : *frame;
649 {
"resolution", resolution},
660 writer.
write(videoImage);
663 for (
auto &nodeManager : m_NodeManagers)
665 nodeManager->media()->sendBinaryMessage(image);
672void Media::registerCameras()
676 for(
auto &oneDevice : INDIListener::devices())
678 auto camera = oneDevice->getCamera();
681 camera->disconnect(
this);
682 connect(camera, &ISD::Camera::newVideoFrame,
this, &Media::sendVideoFrame);
686 auto captureModule = m_Manager->captureModule();
690 auto process = captureModule->process().get();
691 process->disconnect(
this);
696 sendView(view, uuid);
700void Media::resetPolarView()
702 this->correctionVector =
QLineF();
703 m_Manager->alignModule()->zoomAlignView();
706void Media::uploadMetadata(
const QByteArray &metadata)
708 for (
auto &nodeManager : m_NodeManagers)
710 nodeManager->media()->sendTextMessage(metadata);
714void Media::uploadImage(
const QByteArray &image)
716 for (
auto &nodeManager : m_NodeManagers)
718 nodeManager->media()->sendBinaryMessage(image);
722void Media::processNewBLOB(IBLOB * bp)
729 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false)
732 if (qobject_cast<Ekos::Align*>(sender()) == m_Manager->alignModule())
733 sendView(view,
"+A");
734 else if (qobject_cast<Ekos::FocusModule*>(sender()) == m_Manager->focusModule())
735 sendView(view,
"+F");
736 else if (qobject_cast<Ekos::Guide*>(sender()) == m_Manager->guideModule())
737 sendView(view,
"+G");
738 else if (qobject_cast<Ekos::DarkLibrary*>(sender()) == Ekos::DarkLibrary::Instance())
739 sendView(view,
"+D");
Align class handles plate-solving and polar alignment measurement and correction using astrometry....
bool loadAndSlew(const QByteArray &image, const QString &extension)
DBUS interface function.
SkyMapComposite * skyComposite()
static KStars * Instance()
SkyObject * findByName(const QString &name, bool exact=true) override
Search the children of this SkyMapComposite for a SkyObject whose name matches the argument.
Provides all necessary information about an object in the sky: its coordinates, name(s),...
virtual QString name(void) const
The sky coordinates of a point in the sky.
Generic record interfaces and implementations.
QString name(GameStandardAction id)
GeoCoordinates geo(const QVariant &location)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QStringView level(QStringView ifopt)
QAction * zoom(const QObject *recvr, const char *slot, QObject *parent)
const char * constData() const const
QByteArray left(qsizetype len) const const
QByteArray leftJustified(qsizetype width, char fill, bool truncate) const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QByteArray toHex(char separator) const const
QByteArray hash(QByteArrayView data, Algorithm method)
void fill(Qt::GlobalColor color)
bool save(QIODevice *device, const char *format, int quality) const const
QImage scaledToWidth(int width, Qt::TransformationMode mode) const const
void setColor(int index, QRgb colorValue)
void setColorCount(int colorCount)
void setCompression(int compression)
void setDevice(QIODevice *device)
bool write(const QImage &image)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QByteArray toJson(JsonFormat format) const const
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
QPixmap copy(const QRect &rectangle) const const
bool save(QIODevice *device, const char *format, int quality) const const
void start(OpenMode mode)
bool waitForFinished(int msecs)
QRect intersected(const QRect &rectangle) const const
void moveTo(const QPoint &position)
void setSize(const QSize &size)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QByteArray toLatin1() const const
QString toLower() const const
QTextStream & center(QTextStream &stream)
QFuture< T > run(Function function,...)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString toString(StringFormat mode) const const