Skip to content

File CameraManager.cpp

File List > CameraManager > CameraManager.cpp

Go to the documentation of this file

#include "CameraManager.hpp"

#include <cstdio>
#include <cstring>

#include "Utils/SimSatClient.hpp"

namespace Orion {

// Image buffer size must match the BufferManager pool allocation.
static constexpr FwSizeType IMAGE_BUFFER_SIZE = 512 * 512 * 3;  // 786,432 bytes
static constexpr U32 IMAGE_WIDTH = 512;
static constexpr U32 IMAGE_HEIGHT = 512;
static constexpr U32 MIN_CAPTURE_INTERVAL = 65;  // Must exceed worst-case inference time (~60s)

static const char* modeStr(MissionMode mode) {
    switch (mode.e) {
        case MissionMode::IDLE:
            return "IDLE";
        case MissionMode::MEASURE:
            return "MEASURE";
        case MissionMode::DOWNLINK:
            return "DOWNLINK";
        case MissionMode::SAFE:
            return "SAFE";
        default:
            return "UNKNOWN";
    }
}

CameraManager::CameraManager(const char* compName)
    : CameraManagerComponentBase(compName),
      m_imagesCaptured(0),
      m_capturesFailed(0),
      m_autoCaptureEnabled(false),
      m_autoCaptureInterval(MIN_CAPTURE_INTERVAL),
      m_schedCounter(0),
      m_currentMode(MissionMode::IDLE) {}

CameraManager::~CameraManager() {}

// ---------------------------------------------------------------------------
// Command handlers
// ---------------------------------------------------------------------------

void CameraManager::TRIGGER_CAPTURE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
    if (m_currentMode.e != MissionMode::MEASURE) {
        this->log_WARNING_LO_CommandRejectedWrongMode(Fw::String(modeStr(m_currentMode)));
        this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
        return;
    }
    doCapture();
    this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}

void CameraManager::ENABLE_AUTO_CAPTURE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, U32 interval) {
    if (m_currentMode.e != MissionMode::MEASURE) {
        this->log_WARNING_LO_CommandRejectedWrongMode(Fw::String(modeStr(m_currentMode)));
        this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
        return;
    }
    m_autoCaptureEnabled = true;
    if (interval < MIN_CAPTURE_INTERVAL) {
        this->log_WARNING_LO_CaptureIntervalClamped(interval, MIN_CAPTURE_INTERVAL);
        m_autoCaptureInterval = MIN_CAPTURE_INTERVAL;
    } else {
        m_autoCaptureInterval = interval;
    }
    m_schedCounter = 0;

    this->log_ACTIVITY_HI_AutoCaptureEnabled(m_autoCaptureInterval);
    this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}

void CameraManager::DISABLE_AUTO_CAPTURE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
    m_autoCaptureEnabled = false;
    m_schedCounter = 0;

    this->log_ACTIVITY_HI_AutoCaptureDisabled();
    this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}

// ---------------------------------------------------------------------------
// Schedule handler — rate group driven auto-capture
// ---------------------------------------------------------------------------

void CameraManager::modeChangeIn_handler(FwIndexType portNum, const Orion::MissionMode& mode) {
    m_currentMode = mode;

    if (mode.e == MissionMode::MEASURE) {
        // Auto-enable capture on MEASURE entry
        m_autoCaptureEnabled = true;
        m_schedCounter = 0;
        this->log_ACTIVITY_HI_AutoCaptureEnabled(m_autoCaptureInterval);
    } else if (m_autoCaptureEnabled) {
        // Stop capturing in any other mode
        m_autoCaptureEnabled = false;
        m_schedCounter = 0;
        this->log_ACTIVITY_HI_AutoCaptureDisabled();
    }
}

void CameraManager::schedIn_handler(FwIndexType portNum, U32 context) {
    // Only auto-capture in MEASURE mode
    if (!m_autoCaptureEnabled || m_currentMode.e != MissionMode::MEASURE) {
        return;
    }

    m_schedCounter++;
    if (m_schedCounter >= m_autoCaptureInterval) {
        m_schedCounter = 0;
        doCapture();
    }
}

// ---------------------------------------------------------------------------
// Core capture-fuse-dispatch pipeline
// ---------------------------------------------------------------------------

void CameraManager::doCapture() {
    // 1. Check out a buffer from the pool.
    Fw::Buffer buf = this->bufferGetOut_out(0, IMAGE_BUFFER_SIZE);

    if (buf.getSize() == 0) {
        m_capturesFailed++;
        this->tlmWrite_CapturesFailed(m_capturesFailed);
        this->log_WARNING_HI_BufferPoolExhausted();
        return;
    }

    // 2. Fill the buffer with camera data (SimSat or test fallback).
    if (!captureIntoBuffer(buf)) {
        this->bufferReturnOut_out(0, buf);

        m_capturesFailed++;
        this->tlmWrite_CapturesFailed(m_capturesFailed);

        this->log_WARNING_HI_CameraHardwareError();
        return;
    }

    // 3. Pull GPS coordinates from NavTelemetry synchronously.
    NavState nav = this->navStateOut_out(0);

    // 4. Dispatch buffer + coordinates to VlmInferenceEngine asynchronously.
    this->inferenceRequestOut_out(0, buf, nav.get_lat(), nav.get_lon());

    // 5. Update telemetry.
    m_imagesCaptured++;
    this->tlmWrite_ImagesCaptured(m_imagesCaptured);
    this->log_ACTIVITY_HI_ImageDispatched(nav.get_lat(), nav.get_lon());
}

// ---------------------------------------------------------------------------
// Camera capture — fetch from SimSat Mapbox API
// ---------------------------------------------------------------------------

bool CameraManager::captureIntoBuffer(Fw::Buffer& buf) {
    if (SimSatClient::fetchMapboxImage(buf.getData(), IMAGE_WIDTH, IMAGE_HEIGHT)) {
        return true;
    }

    this->log_ACTIVITY_LO_SimSatImageUnavailable();
    return false;
}

}  // namespace Orion