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)
 
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)
 
QString name(StandardAction id)
 
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)