This commit is contained in:
Timur A. Fatkhullin 2024-12-02 19:17:24 +03:00
parent db79062506
commit 2cb6cc2926
2 changed files with 188 additions and 10 deletions

View File

@ -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()

View File

@ -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>