Implementing External Trigger Acquisition with Dalsa Line Scan Cameras Using Sapera LT

System Architecture and Hardware Configuration

This implementation demonstrates the integration of a Dalsa line scan camera with an external hardware trigger signal using C++ and the Qt framework. The reference environment operates on Windows 11, utilizing Qt 5.15.2, the MSVC 2019 64-bit compiler, and the Sapera LT SDK 8.20. The physical setup pairs an LA-CM-16K05A-00-R line scan sensor with an Xtium-CL MX4 frame grabber.

External Trigger Signal Processing

External triggering relies on precise timing between a master controller and the frame grabber. Unlike internal (software) triggering, which initiates caputres on demand, the external mode requires the grabber to listan for discrete electrical pulses (PSO signals) routed from an external PLC or motion controller. The SDK must be configured to detect specific signal edges (rising, falling, high, or low) to initiate acquisition.

A critical operational constraint involves memory buffer allocation. The SDK requires active buffer resources before any external pulse arrives. Failing to queue the transfer pipeline will cause the grabber to discard incoming frames. The correct workflow mandates starting the acquisition state, waiting for the hardware trigger, capturing the line data, and subsequently freezing the transfer once the operation sequence completes. This approach reuses the internal trigger architecture with minimal logic modifications, focusing primarily on buffer lifecycle management and signal synchronization.

Class Interface Definition

The following C++ interface abstracts the Sapera LT components into a structured controller. Variable naming and method signatures have been refactored to align with modern industrial vision standards while preserving SDK functionality.

#include <QObject>
#include <SapAcqDevice.h>
#include <SapBufferWithTrash.h>
#include <SapTransfer.h>

enum class AcquisitionTrigger {
    SoftwareInternal,
    HardwareExternal
};

class LineScanCaptureController : public QObject
{
    Q_OBJECT
public:
    explicit LineScanCaptureController(QObject* parent = nullptr);
    ~LineScanCaptureController() override;

    bool initializeGrabber(const char* serverId, const char* configPath);
    bool primeBuffers();
    bool startContinuousStreaming();
    bool stopStreaming();
    bool captureFrameSequence(int count);
    void configureSignalSource(AcquisitionTrigger type);
    void setStoragePaths(const std::string& ccfDir, const std::string& outputDir);
    void releaseHardware();
    void persistFrameToDisk();

private:
    std::string m_ConfigFilePath;
    std::string m_OutputDirectory;
    const char* m_ServerIdentifier{nullptr};
    AcquisitionTrigger m_SignalMode{AcquisitionTrigger::HardwareExternal};

    SapAcquisition* m_DeviceHandle{nullptr};
    SapBufferWithTrash* m_FramePool{nullptr};
    SapTransfer* m_TransferPipeline{nullptr};
};

Implementation Workflow

The acquisition sequence follows a deterministic path: enumerate connected grabbers, load the hardware profile generated via CamExpert, instantiate the buffer pool and transfer objects, and register callback handlers for frame processing.

Path Initialization

Configuration and storage directory are decoupled from the core logic, enabling flexible deployment across different workstation layouts.

void LineScanCaptureController::setStoragePaths(const std::string& ccfDir, const std::string& outputDir)
{
    m_ConfigFilePath = ccfDir;
    m_OutputDirectory = outputDir;
}

// Usage example during system boot:
// controller.setStoragePaths("E:\\VisionConfig\\ExtTrigger.ccf", "D:\\InspectionData\\");

Continuous Streaming Control

The streaming initiation method routes execution to the buffer priming routine. The underlying transfer engine manages the wait state until the external hardware signal is received.

bool LineScanCaptureController::startContinuousStreaming()
{
    m_FrameIndex = 0;
    bool pipelineActive = primeBuffers();
    if (pipelineActive) {
        qDebug() << "Transfer pipeline initialized and awaiting trigger.";
    } else {
        qWarning() << "Buffer allocation failed. Check device connections.";
    }
    return pipelineActive;
}

Trigger Source Selection

Switching between internal and external modes updates the controller state. The physical signal routing and debounce parameters must be predefined within the loaded .ccf profile.

void LineScanCaptureController::configureSignalSource(AcquisitionTrigger type)
{
    m_SignalMode = type;
}

Image Persistence

The frame export routine utilizes the SDK's native buffer serialization. Filenames are generated dynamically using a zero-padded counter and the preconfigured output directory.

void LineScanCaptureController::persistFrameToDisk()
{
    if (m_OutputDirectory.empty()) {
        qWarning() << "Output directory path is undefined.";
        return;
    }

    std::ostringstream pathBuilder;
    pathBuilder << m_OutputDirectory << std::setfill('0') << std::setw(6)
                << m_FrameIndex << ".bmp";

    std::string targetPath = pathBuilder.str();
    bool writeSuccess = m_FramePool->Save(targetPath.c_str(), "-format bmp");

    if (writeSuccess) {
        qDebug() << "Saved frame:" << QString::fromStdString(targetPath);
        m_FrameIndex++;
    } else {
        qWarning() << "Failed to write frame to disk. Check storage permissions.";
    }
}

Maintaining an active transfer pipeline prior to pulse arrival guarantees deterministic frame capture and prevents buffer overflow during high-speed conveyor operations. The decoupled architecture supports straightforward expansion into multi-camera synchronization or encoder-based position tracking systems.

Tags: dalsa-sapera line-scan-camera qt-cpp external-trigger frame-grabber

Posted on Wed, 14 Oct 2026 16:18:22 +0000 by malec