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"
25#include "ekos_debug.h"
29#include <QtConcurrent>
39 m_Manager(manager), m_NodeManagers(nodeManagers)
41 for (
auto &nodeManager : m_NodeManagers)
43 connect(nodeManager->media(), &Node::connected,
this, &Media::onConnected);
44 connect(nodeManager->media(), &Node::disconnected,
this, &Media::onDisconnected);
45 connect(nodeManager->media(), &Node::onTextReceived,
this, &Media::onTextReceived);
46 connect(nodeManager->media(), &Node::onBinaryReceived,
this, &Media::onBinaryReceived);
49 connect(
this, &Media::newMetadata,
this, &Media::uploadMetadata);
53 m_TemporaryView.clear();
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)
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)
292 m_TemporaryView.reset(
new FITSView());
293 m_TemporaryView->loadData(data);
302 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false)
308 connect(previewImage.get(), &FITSView::loaded,
this, [
this, previewImage]()
310 QtConcurrent::run(this, &Media::upload, previewImage);
312 previewImage->loadFile(filename);
320 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false)
341 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0);
342 imageData->getRecordValue(
"XBINNING", xbin);
343 imageData->getRecordValue(
"YBINNING", ybin);
344 imageData->getRecordValue(
"EXPTIME", exposure);
345 imageData->getRecordValue(
"GAIN", gain);
346 imageData->getRecordValue(
"PIXSIZE1", pixel_size);
347 imageData->getRecordValue(
"FOCALLEN", focal_length);
348 imageData->getRecordValue(
"APTDIA", aperture);
350 auto stretchParameters = view->getStretchParams();
353 const double binned_pixel = pixel_size.toDouble() * xbin.toInt();
357 {
"resolution", resolution},
359 {
"channels", imageData->channels()},
360 {
"mean", imageData->getAverageMean()},
361 {
"median", imageData->getAverageMedian()},
362 {
"stddev", imageData->getAverageStdDev()},
363 {
"min", imageData->getMin()},
364 {
"max", imageData->getMax()},
365 {
"bin",
QString(
"%1x%2").
arg(xbin.toString(), ybin.toString())},
368 {
"exposure", exposure.toString()},
369 {
"focal_length", focal_length.toString()},
370 {
"aperture", aperture.toString()},
371 {
"gain", gain.toString()},
373 {
"shadows", stretchParameters.grey_red.shadows},
374 {
"midtones", stretchParameters.grey_red.midtones},
375 {
"highlights", stretchParameters.grey_red.highlights},
376 {
"hasWCS", imageData->hasWCS()},
377 {
"hfr", imageData->getHFR()},
378 {
"view", view->objectName()},
389 auto fastImage = (!Options::ekosLiveHighBandwidth() || m_UUID[0] ==
"+");
390 auto scaleWidth = fastImage ? HB_IMAGE_WIDTH / 2 : HB_IMAGE_WIDTH;
394 QPixmap scaledImage = view->getDisplayPixmap().width() > scaleWidth ?
396 view->getDisplayPixmap();
401 emit newImage(jpegData);
419 const int32_t width = imageData->width();
420 const int32_t height = imageData->height();
423 QVariant xbin(1), ybin(1), exposure(0), focal_length(0), gain(0), pixel_size(0), aperture(0);
424 imageData->getRecordValue(
"XBINNING", xbin);
425 imageData->getRecordValue(
"YBINNING", ybin);
426 imageData->getRecordValue(
"EXPTIME", exposure);
427 imageData->getRecordValue(
"GAIN", gain);
428 imageData->getRecordValue(
"PIXSIZE1", pixel_size);
429 imageData->getRecordValue(
"FOCALLEN", focal_length);
430 imageData->getRecordValue(
"APTDIA", aperture);
433 const double binned_pixel = pixel_size.toDouble() * xbin.toInt();
437 {
"resolution", resolution},
439 {
"channels", imageData->channels()},
440 {
"mean", imageData->getAverageMean()},
441 {
"median", imageData->getAverageMedian()},
442 {
"stddev", imageData->getAverageStdDev()},
443 {
"bin",
QString(
"%1x%2").
arg(xbin.toString()).
arg(ybin.toString())},
446 {
"exposure", exposure.toString()},
447 {
"focal_length", focal_length.toString()},
448 {
"aperture", aperture.toString()},
449 {
"gain", gain.toString()},
464 if (correctionVector.isNull() ==
false)
466 scaledImage = view->getDisplayPixmap();
467 const double currentZoom = view->getCurrentZoom();
468 const double normalizedZoom = currentZoom / 100;
470 if (fabs(normalizedZoom - 1) > 0.001)
471 scaledImage = view->getDisplayPixmap().scaledToWidth(view->zoomedWidth());
473 scaledImage = view->getDisplayPixmap();
475 QPointF center = 0.5 * correctionVector.p1() * normalizedZoom + 0.5 * correctionVector.p2() * normalizedZoom;
476 uint32_t length = qMax(correctionVector.length() / normalizedZoom, 100 / normalizedZoom);
478 QRect boundingRectable;
479 boundingRectable.
setSize(
QSize(length * 2, length * 2));
481 boundingRectable.
moveTo(topLeft);
484 emit newBoundingRect(boundingRectable, scaledImage.
size(), currentZoom);
486 scaledImage = scaledImage.
copy(boundingRectable);
490 scaledImage = view->getDisplayPixmap().width() > HB_IMAGE_WIDTH / 2 ?
492 view->getDisplayPixmap();
498 emit newImage(jpegData);
506 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false || !frame)
509 int32_t width = Options::ekosLiveHighBandwidth() ? HB_VIDEO_WIDTH : HB_VIDEO_WIDTH / 2;
514 QImage videoImage = (frame->width() > width) ? frame->scaledToWidth(width) : *frame;
523 {
"resolution", resolution},
534 writer.
write(videoImage);
537 for (
auto &nodeManager : m_NodeManagers)
539 nodeManager->media()->sendBinaryMessage(image);
546void Media::registerCameras()
551 auto camera = oneDevice->getCamera();
554 camera->disconnect(
this);
555 connect(camera, &ISD::Camera::newVideoFrame,
this, &Media::sendVideoFrame);
559 auto captureModule = m_Manager->captureModule();
563 auto process = captureModule->process().get();
564 process->disconnect(
this);
570 sendView(view, uuid);
574void Media::resetPolarView()
576 this->correctionVector =
QLineF();
577 m_Manager->alignModule()->zoomAlignView();
580void Media::uploadMetadata(
const QByteArray &metadata)
582 for (
auto &nodeManager : m_NodeManagers)
584 nodeManager->media()->sendTextMessage(metadata);
588void Media::uploadImage(
const QByteArray &image)
590 for (
auto &nodeManager : m_NodeManagers)
592 nodeManager->media()->sendBinaryMessage(image);
596void Media::processNewBLOB(IBLOB * bp)
603 if (Options::ekosLiveImageTransfer() ==
false || m_sendBlobs ==
false)
606 if (qobject_cast<Ekos::Align*>(sender()) == m_Manager->alignModule())
607 sendView(view,
"+A");
608 else if (qobject_cast<Ekos::Focus*>(sender()) == m_Manager->focusModule())
609 sendView(view,
"+F");
610 else if (qobject_cast<Ekos::Guide*>(sender()) == m_Manager->guideModule())
611 sendView(view,
"+G");
612 else if (qobject_cast<Ekos::DarkLibrary*>(sender()) == Ekos::DarkLibrary::Instance())
613 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.
INDIListener is responsible for creating ISD::GDInterface generic devices as new devices arrive from ...
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 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