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>
40Media::Media(Ekos::Manager * manager, QVector<QSharedPointer<NodeManager>> &nodeManagers):
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);
60bool Media::isConnected()
const
62 return std::any_of(m_NodeManagers.begin(), m_NodeManagers.end(), [](
auto & nodeManager)
64 return nodeManager->media()->isConnected();
71void Media::onConnected()
73 auto node = qobject_cast<Node*>(sender());
77 qCInfo(KSTARS_EKOS) <<
"Connected to Media Websocket server at" << node->url().toDisplayString();
85void Media::onDisconnected()
87 auto node = qobject_cast<Node*>(sender());
91 qCInfo(KSTARS_EKOS) <<
"Disconnected from Message Websocket server at" << node->url().toDisplayString();
93 if (isConnected() ==
false)
97 for (
const QString &oneFile : temporaryFiles)
99 temporaryFiles.clear();
108void Media::onTextReceived(
const QString &message)
110 qCInfo(KSTARS_EKOS) <<
"Media Text Websocket Message" << message;
115 qCWarning(KSTARS_EKOS) <<
"Ekos Live Parsing Error" <<
error.errorString();
120 const QString command = msgObj[
"type"].toString();
121 const QJsonObject payload = msgObj[
"payload"].toObject();
123 if (command == commands[ALIGN_SET_FILE_EXTENSION])
124 extension = payload[
"ext"].toString();
125 else if (command == commands[SET_BLOBS])
126 m_sendBlobs = msgObj[
"payload"].toBool();
128 else if (command == commands[ASTRO_GET_OBJECTS_IMAGE])
130 int level = payload[
"level"].toInt(5);
131 double zoom = payload[
"zoom"].toInt(20000);
132 bool exact = payload[
"exact"].toBool(
false);
135 QVariantList objectNames = payload[
"names"].toArray().toVariantList();
137 for (
auto &oneName : objectNames)
140 SkyObject *oneObject = KStarsData::Instance()->skyComposite()->findByName(name, exact);
144 double fov_w = 0, fov_h = 0;
146 if (oneObject->
type() == SkyObject::MOON || oneObject->
type() == SkyObject::PLANET)
151 <<
"--num_times" <<
"1"
152 <<
"--geometry" <<
QString(
"%1x%2").arg(HIPS_TILE_WIDTH).arg(HIPS_TILE_HEIGHT)
154 <<
"--output" << output);
156 centerImage.load(output);
159 HIPSFinder::Instance()->render(oneObject, level, zoom, ¢erImage, fov_w, fov_h);
161 if (!centerImage.isNull())
171 {
"name", exact ?
name : oneObject->
name()},
173 {
"resolution",
QString(
"%1x%2").
arg(HIPS_TILE_WIDTH).
arg(HIPS_TILE_HEIGHT)},
189 centerImage.save(&buffer,
"jpg", 90);
192 emit newImage(jpegData);
197 else if (command == commands[ASTRO_GET_SKYPOINT_IMAGE])
199 int level = payload[
"level"].toInt(5);
200 double zoom = payload[
"zoom"].toInt(20000);
201 double ra = payload[
"ra"].toDouble(0);
202 double de = payload[
"de"].toDouble(0);
203 double width = payload[
"width"].toDouble(512);
204 double height = payload[
"height"].toDouble(512);
208 SkyPoint J2000Coord(coords.ra(), coords.dec());
210 coords.setRA0(J2000Coord.ra());
211 coords.setDec0(J2000Coord.dec());
212 coords.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->
geo()->lat());
214 volatile auto jnowRAString = coords.ra().toHMSString();
215 volatile auto jnowDEString = coords.dec().toDMSString();
216 volatile auto j2000RAString = coords.ra0().toHMSString();
217 volatile auto j2000DEString = coords.dec0().toDMSString();
220 double fov_w = 0, fov_h = 0;
221 HIPSFinder::Instance()->render(&coords, level, zoom, ¢erImage, fov_w, fov_h);
223 if (!centerImage.isNull())
230 {
"uuid",
"skypoint_hips"},
231 {
"name",
"skypoint_hips"},
249 centerImage.save(&buffer,
"jpg", 95);
252 emit newImage(jpegData);
260void Media::onBinaryReceived(
const QByteArray &message)
266 QString metadataString = message.
left(METADATA_PACKET);
279 sendData(data,
"+D");
287 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false || isConnected() ==
false)
290 StretchParams params;
292 stretch(data, image, params);
293 upload(data, image, params, uuid);
301 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false || isConnected() ==
false)
305 data->loadFromFile(filename);
306#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
318 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false || isConnected() ==
false)
330 previewImage->loadData(data);
331 upload(previewImage, uuid);
339 double min = 0, max = 0;
340 data->getMinMax(&min, &max);
341 auto width = data->width();
342 auto height = data->height();
343 auto channels = data->channels();
344 auto dataType = data->dataType();
356 for (
int i = 0; i < 256; i++)
364 Stretch stretch(width, height, channels, dataType);
367 params = stretch.computeParams(data->getImageBuffer());
368 stretch.setParams(params);
369 stretch.run(data->getImageBuffer(), &image, 1);
385 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0);
386 imageData->getRecordValue(
"XBINNING", xbin);
387 imageData->getRecordValue(
"YBINNING", ybin);
388 imageData->getRecordValue(
"EXPTIME", exposure);
389 imageData->getRecordValue(
"GAIN", gain);
390 imageData->getRecordValue(
"PIXSIZE1", pixel_size);
391 imageData->getRecordValue(
"FOCALLEN", focal_length);
392 imageData->getRecordValue(
"APTDIA", aperture);
394 auto stretchParameters = view->getStretchParams();
397 const double binned_pixel = pixel_size.toDouble() * xbin.toInt();
402 {
"resolution", resolution},
404 {
"channels", imageData->channels()},
405 {
"mean", imageData->getAverageMean()},
406 {
"median", imageData->getAverageMedian()},
407 {
"stddev", imageData->getAverageStdDev()},
408 {
"min", imageData->getMin()},
409 {
"max", imageData->getMax()},
410 {
"bin",
QString(
"%1x%2").
arg(xbin.toString(), ybin.toString())},
413 {
"exposure", exposure.toString()},
414 {
"focal_length", focal_length.toString()},
415 {
"aperture", aperture.toString()},
416 {
"gain", gain.toString()},
418 {
"shadows", stretchParameters.grey_red.shadows},
419 {
"midtones", stretchParameters.grey_red.midtones},
420 {
"highlights", stretchParameters.grey_red.highlights},
421 {
"hasWCS", imageData->hasWCS()},
422 {
"hfr", imageData->getHFR()},
423 {
"view", view->objectName()},
434 auto fastImage = (!Options::ekosLiveHighBandwidth() || uuid[0] ==
'+');
435 auto scaleWidth = fastImage ? HB_IMAGE_WIDTH / 2 : HB_IMAGE_WIDTH;
439 QPixmap scaledImage = view->getDisplayPixmap().width() > scaleWidth ?
441 view->getDisplayPixmap();
446 emit newImage(jpegData);
462 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0);
463 data->getRecordValue(
"XBINNING", xbin);
464 data->getRecordValue(
"YBINNING", ybin);
465 data->getRecordValue(
"EXPTIME", exposure);
466 data->getRecordValue(
"GAIN", gain);
467 data->getRecordValue(
"PIXSIZE1", pixel_size);
468 data->getRecordValue(
"FOCALLEN", focal_length);
469 data->getRecordValue(
"APTDIA", aperture);
472 const double binned_pixel = pixel_size.toDouble() * xbin.toInt();
477 {
"resolution", resolution},
479 {
"channels", data->channels()},
480 {
"mean", data->getAverageMean()},
481 {
"median", data->getAverageMedian()},
482 {
"stddev", data->getAverageStdDev()},
483 {
"min", data->getMin()},
484 {
"max", data->getMax()},
485 {
"bin",
QString(
"%1x%2").
arg(xbin.toString(), ybin.toString())},
488 {
"exposure", exposure.toString()},
489 {
"focal_length", focal_length.toString()},
490 {
"aperture", aperture.toString()},
491 {
"gain", gain.toString()},
493 {
"shadows", params.grey_red.shadows},
494 {
"midtones", params.grey_red.midtones},
495 {
"highlights", params.grey_red.highlights},
496 {
"hasWCS", data->hasWCS()},
497 {
"hfr", data->getHFR()},
508 auto fastImage = (!Options::ekosLiveHighBandwidth() || uuid[0] ==
'+');
509 auto scaleWidth = fastImage ? HB_IMAGE_WIDTH / 2 : HB_IMAGE_WIDTH;
520 emit newImage(jpegData);
528 if (isConnected() ==
false)
541 const int32_t width = imageData->width();
542 const int32_t height = imageData->height();
545 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0);
546 imageData->getRecordValue(
"XBINNING", xbin);
547 imageData->getRecordValue(
"YBINNING", ybin);
548 imageData->getRecordValue(
"EXPTIME", exposure);
549 imageData->getRecordValue(
"GAIN", gain);
550 imageData->getRecordValue(
"PIXSIZE1", pixel_size);
551 imageData->getRecordValue(
"FOCALLEN", focal_length);
552 imageData->getRecordValue(
"APTDIA", aperture);
555 const double binned_pixel = pixel_size.toDouble() * xbin.toInt();
559 {
"resolution", resolution},
561 {
"channels", imageData->channels()},
562 {
"mean", imageData->getAverageMean()},
563 {
"median", imageData->getAverageMedian()},
564 {
"stddev", imageData->getAverageStdDev()},
565 {
"bin",
QString(
"%1x%2").
arg(xbin.toString()).
arg(ybin.toString())},
568 {
"exposure", exposure.toString()},
569 {
"focal_length", focal_length.toString()},
570 {
"aperture", aperture.toString()},
571 {
"gain", gain.toString()},
586 if (correctionVector.isNull() ==
false)
588 scaledImage = view->getDisplayPixmap();
589 const double currentZoom = view->getCurrentZoom();
590 const double normalizedZoom = currentZoom / 100;
592 if (fabs(normalizedZoom - 1) > 0.001)
593 scaledImage = view->getDisplayPixmap().scaledToWidth(view->zoomedWidth());
595 scaledImage = view->getDisplayPixmap();
597 QPointF center = 0.5 * correctionVector.p1() * normalizedZoom + 0.5 * correctionVector.p2() * normalizedZoom;
598 uint32_t length = qMax(correctionVector.length() / normalizedZoom, 100 / normalizedZoom);
600 QRect boundingRectable;
601 boundingRectable.
setSize(
QSize(length * 2, length * 2));
603 boundingRectable.
moveTo(topLeft);
606 emit newBoundingRect(boundingRectable, scaledImage.
size(), currentZoom);
608 scaledImage = scaledImage.
copy(boundingRectable);
612 scaledImage = view->getDisplayPixmap().width() > HB_IMAGE_WIDTH / 2 ?
614 view->getDisplayPixmap();
620 emit newImage(jpegData);
628 if (isConnected() ==
false ||
629 Options::ekosLiveImageTransfer() ==
false ||
630 m_sendBlobs ==
false ||
634 int32_t width = Options::ekosLiveHighBandwidth() ? HB_VIDEO_WIDTH : HB_VIDEO_WIDTH / 2;
639 QImage videoImage = (frame->width() > width) ? frame->scaledToWidth(width) : *frame;
648 {
"resolution", resolution},
659 writer.
write(videoImage);
662 for (
auto &nodeManager : m_NodeManagers)
664 nodeManager->media()->sendBinaryMessage(image);
671void Media::registerCameras()
675 for(
auto &oneDevice : INDIListener::devices())
677 auto camera = oneDevice->getCamera();
680 camera->disconnect(
this);
681 connect(camera, &ISD::Camera::newVideoFrame,
this, &Media::sendVideoFrame);
685 auto captureModule = m_Manager->captureModule();
689 auto process = captureModule->process().get();
690 process->disconnect(
this);
693 sendView(view, view->imageData()->objectName());
697void Media::resetPolarView()
699 this->correctionVector =
QLineF();
700 m_Manager->alignModule()->zoomAlignView();
703void Media::uploadImage(
const QByteArray &image)
705 for (
auto &nodeManager : m_NodeManagers)
707 nodeManager->media()->sendBinaryMessage(image);
711void Media::processNewBLOB(IBLOB * bp)
718 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false)
721 if (qobject_cast<Ekos::Align*>(sender()) == m_Manager->alignModule())
722 sendView(view,
"+A");
723 else if (qobject_cast<Ekos::Focus*>(sender()) == m_Manager->focusModule()->mainFocuser())
724 sendView(view,
"+F");
725 else if (qobject_cast<Ekos::Guide*>(sender()) == m_Manager->guideModule())
726 sendView(view,
"+G");
727 else if (qobject_cast<Ekos::DarkLibrary*>(sender()) == Ekos::DarkLibrary::Instance())
728 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.
static KStars * Instance()
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.
GeoCoordinates geo(const QVariant &location)
QString name(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)
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)