...
This commit is contained in:
parent
db79062506
commit
2cb6cc2926
@ -1,4 +1,3 @@
|
||||
#include <xcliball.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "raptor_eagle_cameralink.h"
|
||||
@ -7,6 +6,62 @@
|
||||
|
||||
#include <common/adc_utils.h>
|
||||
|
||||
namespace details
|
||||
{
|
||||
|
||||
// compute checksum as XOR operation along elements of byte array
|
||||
template <std::ranges::input_range R>
|
||||
auto computeChecksum(const R& bytes, bool final_etx = true)
|
||||
requires std::convertible_to<std::ranges::range_value_t<R>, char>
|
||||
{
|
||||
std::ranges::range_value_t<R> res = 0;
|
||||
|
||||
if (std::ranges::size(bytes) == 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (auto& byte : bytes) {
|
||||
res ^= byte;
|
||||
}
|
||||
|
||||
if (final_etx) {
|
||||
res ^= CL_ETX;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// assume that least significant byte is the last one in 'bytes'
|
||||
template <std::ranges::input_range R>
|
||||
size_t convert40BitToCounts(const R& bytes)
|
||||
requires std::same_as<std::ranges::range_value_t<R>, unsigned char>
|
||||
{
|
||||
size_t counts = 0, i = std::ranges::size(bytes);
|
||||
|
||||
for (auto& byte : bytes) {
|
||||
counts += byte << (--i * 8);
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
|
||||
template <std::ranges::output_range<unsigned char> R>
|
||||
R convertCountsTo40Bit(uint64_t counts)
|
||||
{
|
||||
R res;
|
||||
|
||||
auto sp = std::span(reinterpret_cast<unsigned char*>(&counts), 8);
|
||||
|
||||
// least significant byte in the end of the output range
|
||||
std::ranges::copy(sp | std::views::take(5) | std::views::reverse, std::back_inserter(res));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
|
||||
#define DEFAULT_EPIX_VIDEO_FMT_FILE "raptor_eagle-v.fmt"
|
||||
|
||||
/* CONSTRUCTORS AND DESTRUCTOR */
|
||||
@ -17,8 +72,8 @@ RaptorEagleCCD::RaptorEagleCCD(const adc::traits::adc_input_char_range auto& epi
|
||||
adc::AdcSpdlogLogger(logger),
|
||||
_epixFmtVideoFilename(),
|
||||
_cameraUnitmap(1), // by default only the single camera
|
||||
_clCommandAckBit(1), // enable by default
|
||||
_clChecksumBit(1), // enable by default
|
||||
_clCommandAckBit(1), // enable by default (at camera boot up)
|
||||
_clChecksumBit(1), // enable by default (at camera boot up)
|
||||
_expTime(0.0)
|
||||
|
||||
{
|
||||
@ -111,7 +166,7 @@ size_t RaptorEagleCCD::clRead(byte_seq_t& bytes)
|
||||
bytes.resize(nbytes);
|
||||
}
|
||||
|
||||
xclibApiCall(pxd_serialRead(_cameraUnitmap, 0, bytes.data(), nbytes),
|
||||
xclibApiCall(pxd_serialRead(_cameraUnitmap, 0, (char*)bytes.data(), nbytes),
|
||||
std::format("pxd_serialRead({}, 0, {}, {})", _cameraUnitmap, (void*)bytes.data(), nbytes));
|
||||
|
||||
|
||||
@ -139,9 +194,19 @@ size_t RaptorEagleCCD::clReadAndCheckAck(byte_seq_t& bytes)
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
size_t RaptorEagleCCD::clReadAndCheckAck()
|
||||
{
|
||||
byte_seq_t bytes;
|
||||
|
||||
return clReadAndCheckAck(bytes);
|
||||
}
|
||||
|
||||
|
||||
// 'bytes' must contain only data without trailing ETX and checksum bytes!
|
||||
size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
|
||||
{
|
||||
static unsigned char etx_checksum_bytes[] = {CL_ETX, 0xFF};
|
||||
|
||||
if (bytes.empty()) {
|
||||
logWarn("An empty transmitted byte sequence! Nothing to do!");
|
||||
return 0;
|
||||
@ -155,21 +220,23 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
|
||||
}
|
||||
|
||||
|
||||
size_t nbytes, tr_nbytes = bytes.size();
|
||||
size_t nbytes, tr_nbytes = 1 + _clChecksumBit;
|
||||
|
||||
// how many bytes are available in Tx-buffer
|
||||
xclibApiCall(nbytes = pxd_serialWrite(_cameraUnitmap, 0, nullptr, 0),
|
||||
std::format("pxd_serialWrite({}, 0, NULL, 0)", _cameraUnitmap));
|
||||
|
||||
if (nbytes) {
|
||||
tr_nbytes += _clCommandAckBit + _clChecksumBit;
|
||||
|
||||
if (nbytes < tr_nbytes) {
|
||||
if (nbytes < (bytes.size() + tr_nbytes)) {
|
||||
logWarn(
|
||||
"Not enough of available space in the internal Tx-buffer (needs = {}, available = {})! Nothing to do!",
|
||||
tr_nbytes, nbytes);
|
||||
bytes.size() + tr_nbytes, nbytes);
|
||||
nbytes = 0;
|
||||
} else {
|
||||
if (_clChecksumBit) {
|
||||
etx_checksum_bytes[1] = details::computeChecksum(bytes);
|
||||
}
|
||||
|
||||
xclibApiCall(
|
||||
nbytes = pxd_serialWrite(_cameraUnitmap, 0, (char*)bytes.data(), bytes.size()),
|
||||
std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)bytes.data(), bytes.size()));
|
||||
@ -177,6 +244,23 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
|
||||
if (nbytes != bytes.size()) {
|
||||
throw std::error_code(RaptorEagleCCDError::ERROR_CAMLINK_WRITE);
|
||||
}
|
||||
|
||||
// send trailing ETX and possible checksum bytes
|
||||
size_t n;
|
||||
|
||||
if (tr_nbytes > 1) {
|
||||
logDebug("Write trailing ETX and checksum bytes");
|
||||
} else {
|
||||
logDebug("Write trailing ETX byte");
|
||||
}
|
||||
xclibApiCall(
|
||||
n = pxd_serialWrite(_cameraUnitmap, 0, (char*)etx_checksum_bytes, tr_nbytes),
|
||||
std::format("pxd_serialWrite({}, 0, {}, {})", _cameraUnitmap, (void*)etx_checksum_bytes, tr_nbytes));
|
||||
if (n != tr_nbytes) {
|
||||
throw std::error_code(RaptorEagleCCDError::ERROR_CAMLINK_WRITE);
|
||||
}
|
||||
|
||||
nbytes += n;
|
||||
}
|
||||
} else {
|
||||
logWarn("No available space in the internal Tx-buffer! Nothing to do!");
|
||||
@ -186,6 +270,69 @@ size_t RaptorEagleCCD::clWrite(const byte_seq_t& bytes)
|
||||
}
|
||||
|
||||
|
||||
RaptorEagleCCD::byte_seq_t RaptorEagleCCD::readRegisters(const RaptorEagleCCD::byte_seq_t& addrs,
|
||||
byte_seq_t set_addr_cmd)
|
||||
{
|
||||
// to protect in multi-threading environment (multiple read-write operations, see below)
|
||||
std::lock_guard<std::mutex> lock_guard(_camlinkMutex);
|
||||
|
||||
byte_seq_t reg_vals, ans(3);
|
||||
|
||||
if (addrs.empty()) {
|
||||
logWarn("Registers addresses array is an empty! Nothing to do!");
|
||||
return reg_vals;
|
||||
}
|
||||
|
||||
// from Eagle V 4240 instruction manual (rev 1.1)
|
||||
byte_seq_t set_addr_comm = std::move(set_addr_cmd); // set address controller command
|
||||
static const byte_seq_t read_reg_comm{0x53, 0xE1, 0x01}; // read register controller command
|
||||
|
||||
|
||||
reg_vals.resize(addrs.size());
|
||||
|
||||
size_t i = 0;
|
||||
for (auto& addr : addrs) {
|
||||
// set address
|
||||
set_addr_comm[3] = addr;
|
||||
clWrite(set_addr_comm);
|
||||
clReadAndCheckAck(ans);
|
||||
|
||||
// get value
|
||||
clWrite(read_reg_comm);
|
||||
clReadAndCheckAck(ans);
|
||||
|
||||
reg_vals[i++] = ans[0];
|
||||
}
|
||||
|
||||
return reg_vals;
|
||||
}
|
||||
|
||||
|
||||
void RaptorEagleCCD::writeRegisters(const byte_seq_t& addrs, const byte_seq_t& values)
|
||||
{
|
||||
// to protect in multi-threading environment (multiple read-write operations, see below)
|
||||
std::lock_guard<std::mutex> lock_guard(_camlinkMutex);
|
||||
|
||||
if (addrs.empty() || values.empty()) {
|
||||
logWarn("Registers addresses or values array is an empty! Nothing to do!");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t N = addrs.size() < values.size() ? addrs.size() : values.size();
|
||||
|
||||
// from Eagle V 4240 instruction manual (rev 1.1)
|
||||
byte_seq_t comm{0x53, 0xE0, 0x02, 0x00, 0x00};
|
||||
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
comm[3] = addrs[i];
|
||||
comm[4] = values[i];
|
||||
|
||||
clWrite(comm);
|
||||
clReadAndCheckAck(); // no data from controller here just check answer for errors
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* CREATE COMMANDS AND ATTRIBUTES */
|
||||
|
||||
void RaptorEagleCCD::initAttrComm()
|
||||
|
||||
@ -43,7 +43,21 @@ public:
|
||||
// static constexpr std::string_view CAMERA_CMD_OPEN_SHUTTER{"OPEN_SHUTTER"};
|
||||
// static constexpr std::string_view CAMERA_CMD_CLOSE_SHUTTER{"CLOSE_SHUTTER"};
|
||||
|
||||
// system status byte bits
|
||||
struct SystemStatus {
|
||||
bool checkSumEnabled;
|
||||
bool ackEnabled;
|
||||
bool bootedFPGA;
|
||||
bool holdFPGAInReset;
|
||||
bool commsFPGAEnabled;
|
||||
};
|
||||
|
||||
// FPGA status bits
|
||||
struct FPGAStatus {
|
||||
bool highGainEnabled;
|
||||
bool overTemp;
|
||||
bool TECEnabled;
|
||||
};
|
||||
|
||||
RaptorEagleCCD(const adc::traits::adc_input_char_range auto& epix_video_fmt_filename,
|
||||
std::shared_ptr<spdlog::logger> logger = spdlog::null_logger_mt("EAGLE_CCD_NULLLOGGER"));
|
||||
@ -53,7 +67,7 @@ public:
|
||||
~RaptorEagleCCD();
|
||||
|
||||
private:
|
||||
typedef std::vector<char> byte_seq_t;
|
||||
typedef std::vector<unsigned char> byte_seq_t;
|
||||
|
||||
std::string _epixFmtVideoFilename;
|
||||
int _cameraUnitmap;
|
||||
@ -62,6 +76,8 @@ private:
|
||||
uint8_t _clCommandAckBit;
|
||||
uint8_t _clChecksumBit;
|
||||
|
||||
std::mutex _camlinkMutex;
|
||||
|
||||
// attributes inner variables
|
||||
double _expTime;
|
||||
size_t _frameNumbers;
|
||||
@ -73,13 +89,28 @@ private:
|
||||
void openPIXCI();
|
||||
void closePIXCI();
|
||||
|
||||
|
||||
|
||||
// CameraLink-related low-level methods
|
||||
|
||||
size_t clRead(byte_seq_t& bytes);
|
||||
size_t clReadAndCheckAck(byte_seq_t& bytes);
|
||||
size_t clReadAndCheckAck();
|
||||
|
||||
size_t clWrite(const byte_seq_t& bytes);
|
||||
|
||||
// CameraLink-related registers read/write methods
|
||||
|
||||
// there are two kind of SET-READ_REGISTER-ADDRESS command in the current version of the camera controller firmware:
|
||||
// {0x53, 0xE0, 0x01} and
|
||||
// {0x53, 0xE0, 0x02}
|
||||
//
|
||||
// NOTE: given 'set_addr_cmd' byte sequence must be one-byte longer than the command itself!!!
|
||||
byte_seq_t readRegisters(const byte_seq_t& addrs, byte_seq_t set_addr_cmd = {0x53, 0xE0, 0x01, 0x00});
|
||||
void writeRegisters(const byte_seq_t& addrs, const byte_seq_t& values);
|
||||
|
||||
|
||||
|
||||
// logging helper methods
|
||||
|
||||
template <bool NOEXCEPT = false>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user