Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 171 additions & 90 deletions camerad/archon_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,136 +394,217 @@ namespace Camera {
* @throws std::runtime_error
*
*/
std::vector<ArchonController::bias_bank_t> ArchonController::bias_banks(int modtype) {
switch (modtype) {
case MODTYPE_LVBIAS:
case MODTYPE_LVXBIAS:
return {{"LVLC", 1, 24, -14.0, +14.0, false, false},
{"LVHC", 25, 6, -14.0, +14.0, true, true}};
case MODTYPE_HVBIAS:
case MODTYPE_HVXBIAS:
return {{"HVLC", 1, 24, 0.0, +31.0, false, false},
{"HVHC", 25, 6, 0.0, +31.0, true, true}};
case MODTYPE_XVBIAS:
return {{"XVP", 1, XVBIAS_CHANS_PER_POLARITY,
0.0, XVBIAS_VOLTS_MAGNITUDE, true, false},
{"XVN", 1+XVBIAS_CHANS_PER_POLARITY, XVBIAS_CHANS_PER_POLARITY,
-XVBIAS_VOLTS_MAGNITUDE, 0.0, true, false}};
default:
return {};
}
}


ArchonController::bias_config_t ArchonController::get_bias_config(int mod, int chan) const {
std::ostringstream oss;

// Check that the module number is valid
if ( (mod < 0) || (mod > MAXNMODS) ) {
oss << "module " << mod << ": outside range {0:" << MAXNMODS << "}";
if ( (mod < 1) || (mod > MAXNMODS) ) {
oss << "module " << mod << ": outside range {1:" << MAXNMODS << "}";
throw std::runtime_error(oss.str());
}

// Check that the channel number is valid
if ( (chan < 1) || (chan > 30) ) {
oss << "bias channel " << mod << ": outside range {1:30}";
if (this->modtype[mod-1] == MODTYPE_NONE) {
oss << "module " << mod << " not installed";
throw std::runtime_error(oss.str());
}

bias_config_t info;
std::ostringstream biasconfig;

// Use the module type to get LV or HV Bias
// and start building the bias configuration string.
float vmin, vmax;
switch ( this->modtype[ mod-1 ] ) {
case MODTYPE_NONE:
oss << "module " << mod << " not installed";
throw std::runtime_error(oss.str());
case MODTYPE_LVBIAS:
case MODTYPE_LVXBIAS:
biasconfig << "MOD" << mod << "/LV";
info.vmin = -14.0;
info.vmax = +14.0;
break;
case MODTYPE_HVBIAS:
case MODTYPE_HVXBIAS:
biasconfig << "MOD" << mod << "/HV";
info.vmin = 0.0;
info.vmax = +31.0;
break;
default:
oss << "module " << mod << " not a bias board";
throw std::runtime_error(oss.str());
const auto banks = bias_banks(this->modtype[mod-1]);
if (banks.empty()) {
oss << "module " << mod << " not a bias board";
throw std::runtime_error(oss.str());
}

// append the channel to the bias configuration string
if (chan < 25) {
biasconfig << "LC_V" << chan;
}
else {
biasconfig << "HC_V" << (chan-24);
for (const auto &bank : banks) {
if (chan < bank.first_chan || chan >= bank.first_chan + bank.count) continue;
bias_config_t info;
info.bank = bank;
info.index = chan - bank.first_chan + 1;
info.vmin = bank.vmin;
info.vmax = bank.vmax;
return info;
}

info.key = biasconfig.str();
return info;
oss << "bias channel " << chan << ": outside range {1:"
<< banks.back().first_chan + banks.back().count - 1 << "} for module " << mod;
throw std::runtime_error(oss.str());
}
/***** Camera::ArchonController::get_bias_config ****************************/


/***** Camera::ArchonController::make_applymod_command **********************/
std::string ArchonController::bias_key(int mod, const bias_config_t &info, BiasField field) {
std::ostringstream key;
key << "MOD" << mod << "/" << info.bank.prefix << "_";
switch (field) {
case BiasField::VOLTS: key << "V"; break;
case BiasField::ORDER: key << "ORDER"; break;
case BiasField::ENABLE: key << "ENABLE"; break;
case BiasField::CURRENT_LIMIT: key << "IL"; break;
case BiasField::LABEL: key << "LABEL"; break;
}
key << info.index;
return key.str();
}


/***** Camera::ArchonController::read_bias_channel **************************/
/**
* @brief creates an APPLYMODx string
* @returns std::string
*
* @brief report every attribute one bias channel carries
* @details Values come from configuration memory, so they are what was
* asked of the board, not a measurement of what it is doing.
*/
std::string ArchonController::make_applymod_command(int mod) const {
std::string ArchonController::read_bias_channel(int mod, int chan) const {
const auto info = get_bias_config(mod, chan);

auto value_of = [&](BiasField field) {
const auto it = this->configmap.find(bias_key(mod, info, field));
return it == this->configmap.end() ? std::string() : it->second.value;
};

const std::string label = value_of(BiasField::LABEL);

std::ostringstream oss;
oss << "APPLYMOD"
<< std::setfill('0')
<< std::setw(2)
<< std::hex
<< (mod-1);
oss << "MOD" << mod << " chan " << chan << " " << info.bank.prefix
<< " V=" << value_of(BiasField::VOLTS)
<< " ORDER=" << value_of(BiasField::ORDER);
if (info.bank.has_enable) oss << " ENABLE=" << value_of(BiasField::ENABLE);
if (info.bank.has_current_limit) oss << " IL=" << value_of(BiasField::CURRENT_LIMIT);
oss << " LABEL=\"" << label << "\"";
return oss.str();
}
/***** Camera::ArchonController::make_applymod_command **********************/
/***** Camera::ArchonController::read_bias_channel **************************/


/***** Camera::ArchonController::bias ***************************************/
/***** Camera::ArchonController::set_bias_fields ****************************/
/**
* @brief parse the configuration file for controller-related parameters
* @details The config file has already been read into the Config class.
* @throws std::runtime_error
*
* @brief write one or more attributes of a bias channel, then apply once
* @details Batched because every APPLYMOD is a round trip, so setting
* three attributes separately would apply the module three times.
*/
void ArchonController::bias(const int &mod, const int &chan, float &volts, const bool &should_write) {
const std::string function("Camera::ArchonController::bias");
std::ostringstream oss;
std::ostringstream biasconfig;
long ArchonController::set_bias_fields(int mod, int chan,
const std::vector<std::pair<BiasField,std::string>> &fields,
std::string &retstring) {
const std::string function("Camera::ArchonController::set_bias_fields");

// nothing to do if no connection open to controller
if (!this->archon.isconnected()) {
throw std::runtime_error("connection not open to controller");
}

// creating the bias configuration key also validates mod and chan
auto info = get_bias_config(mod, chan);
const auto info = get_bias_config(mod, chan);
std::ostringstream oss;

// write the bias configuration line if needed
if (should_write) {
bool changed=false;
// check requested voltage is within range
if ( (volts < info.vmin) || (volts > info.vmax) ) {
oss << volts << " outside range {" << info.vmin << ":" << info.vmax << "}";
for (const auto &[field, value] : fields) {
if (field == BiasField::ENABLE && !info.bank.has_enable) {
oss << "bank " << info.bank.prefix << " has no enable";
throw std::runtime_error(oss.str());
}

// write the configuration line to update the bias voltage
std::string val = std::to_string(volts);
this->write_config_key(info.key.c_str(), val.c_str(), changed);

// send the APPLYMODx command
long error = this->send_cmd(make_applymod_command(mod));

if (error != NO_ERROR) {
oss << "writing bias configuration " << info.key << "=" << val;
if (field == BiasField::CURRENT_LIMIT && !info.bank.has_current_limit) {
oss << "bank " << info.bank.prefix << " has no current limit";
throw std::runtime_error(oss.str());
}
else if (!changed) {
oss << "bias configuration " << info.key << "=" << val << " unchanged";
logwrite(function, oss.str());
return;
if (field == BiasField::VOLTS) {
const float volts = std::stof(value);
if (volts < info.vmin || volts > info.vmax) {
oss << volts << " outside range {" << info.vmin << ":" << info.vmax << "}";
throw std::runtime_error(oss.str());
}
}
else {
oss << "updated bias configuration " << info.key << "=" << val;
logwrite(function, oss.str());
return;
if (field == BiasField::CURRENT_LIMIT) {
const int milliamps = std::stoi(value);
if (milliamps < 0 || milliamps > BIAS_CURRENT_LIMIT_MAX_MA) {
oss << milliamps << " outside range {0:" << BIAS_CURRENT_LIMIT_MAX_MA << "} mA";
throw std::runtime_error(oss.str());
}
}
bool changed = false;
this->write_config_key(bias_key(mod, info, field).c_str(), value.c_str(), changed);
}

if (this->send_cmd(make_applymod_command(mod)) != NO_ERROR) {
throw std::runtime_error("applying module "+std::to_string(mod));
}

retstring = read_bias_channel(mod, chan);
logwrite(function, retstring);
return NO_ERROR;
}
/***** Camera::ArchonController::set_bias_fields ****************************/


/***** Camera::ArchonController::list_biases ********************************/
/**
* @brief report every configured bias channel, for one module or all
* @param[in] mod module to report, or 0 for every bias board installed
*/
long ArchonController::list_biases(int mod, std::string &retstring) const {
std::ostringstream oss;

for (int slot = 1; slot <= MAXNMODS; ++slot) {
if (mod != 0 && slot != mod) continue;
if (bias_banks(this->modtype[slot-1]).empty()) continue;

for (const auto &bank : bias_banks(this->modtype[slot-1])) {
for (int i = 0; i < bank.count; ++i) {
const int chan = bank.first_chan + i;
// Skip channels the ACF never defined; the board has them, the
// configuration does not, so there is nothing to report
bias_config_t probe;
probe.bank = bank;
probe.index = i + 1;
if (this->configmap.find(bias_key(slot, probe, BiasField::VOLTS)) ==
this->configmap.end()) continue;
oss << read_bias_channel(slot, chan) << "\n";
}
}
}

// read the configuration
this->get_configmap_value(info.key, volts);
retstring = oss.str();
if (retstring.empty()) {
retstring = (mod == 0) ? "no bias boards installed"
: "module "+std::to_string(mod)+" has no configured bias channels";
}
return NO_ERROR;
}
/***** Camera::ArchonController::list_biases ********************************/
/***** Camera::ArchonController::get_bias_config ****************************/


/***** Camera::ArchonController::make_applymod_command **********************/
/**
* @brief creates an APPLYMODx string
* @returns std::string
*
*/
std::string ArchonController::make_applymod_command(int mod) const {
std::ostringstream oss;
oss << "APPLYMOD"
<< std::setfill('0')
<< std::setw(2)
<< std::hex
<< (mod-1);
return oss.str();
}
/***** Camera::ArchonController::bias ***************************************/
/***** Camera::ArchonController::make_applymod_command **********************/




/***** Camera::ArchonController::initiate_exposure **************************/
Expand Down
41 changes: 37 additions & 4 deletions camerad/archon_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ constexpr int MODTYPE_ADLN = 15;
constexpr int MODTYPE_UNKNOWN = 16;
constexpr int MODTYPE_ADM = 17;

// An XVBias module carries this many positive channels, then as many negative
constexpr int XVBIAS_CHANS_PER_POLARITY = 4;
constexpr float XVBIAS_VOLTS_MAGNITUDE = 95.0f;

// Highest current limit a high-current bias channel accepts
constexpr int BIAS_CURRENT_LIMIT_MAX_MA = 250;

// AD and ADM modules are restricted to slots 5-8, the range RAWSEL addresses
constexpr int AD_SLOT_FIRST = 5;
constexpr int AD_SLOT_LAST = 8;
Expand Down Expand Up @@ -170,13 +177,40 @@ namespace Camera {
* @var struct bias_config_t
* @details structure of bias configuration info
*/
/**
* @var struct bias_bank_t
* @details one run of bias channels sharing a key prefix. The banks of a
* board differ in which attributes they carry, so this says what
* a channel supports rather than the caller assuming.
*/
struct bias_bank_t {
const char* prefix{nullptr}; //!< HVLC, HVHC, XVP, XVN, LVLC, LVHC
int first_chan{0}; //!< channel this bank starts at
int count{0};
float vmin{0.0};
float vmax{0.0};
bool has_enable{false};
bool has_current_limit{false};
};

struct bias_config_t {
std::string key;
float vmin;
float vmax;
float vmin{0.0};
float vmax{0.0};
bias_bank_t bank;
int index{0}; //!< 1-based channel number within the bank
};

/// an attribute of one bias channel, each a separate configuration key
enum class BiasField { VOLTS, ORDER, ENABLE, CURRENT_LIMIT, LABEL };

static std::vector<bias_bank_t> bias_banks(int modtype);
static std::string bias_key(int mod, const bias_config_t &info, BiasField field);
bias_config_t get_bias_config(int mod, int chan) const;
std::string read_bias_channel(int mod, int chan) const;
long set_bias_fields(int mod, int chan,
const std::vector<std::pair<BiasField,std::string>> &fields,
std::string &retstring);
long list_biases(int mod, std::string &retstring) const;
std::string make_applymod_command(int mod) const;

/**
Expand Down Expand Up @@ -342,7 +376,6 @@ namespace Camera {
std::string abort_param; //!< parameter name to abort when set =1 (optional)

void connect();
void bias(const int &mod, const int &chan, float &volts, const bool &should_write);
long initiate_exposure(const int &nexp);
long get_frame_status();
template<typename T> T get_parameter(const std::string &parameter);
Expand Down
Loading
Loading