Commit 74d1803e authored by Alejandro Homs Puron's avatar Alejandro Homs Puron Committed by Laurent Claustre
Browse files

Improve SeqTim measurement management:

* Add ConfigTimingMeasureMap cache with initial pre-calibrated SeqTim
  (readout/transfer time) values, updated on each measurement
* Add needSeqTimMeasure to query the need of measurement, and
  AutoSeqTimMeasure option to auto-trigger it in prepare[Acq]
* Set LatencyTime to 0 when measuring SeqTim and
  set the default sequence timeout to 5 seconds
* Add NbLinesXfer and ShutElecSelect to cached registers
* Tango reading of readout/transfer_time fails if needSeqTimMeasure
* Wait for end of SPB EspiaXfer in measureSeqTimValues
parent 5e6d540e
......@@ -57,7 +57,8 @@ class Camera
public:
AcqSeq(Camera& cam);
~AcqSeq();
bool wait(double timeout);
bool wait(double timeout,
bool use_ser_line=false, bool read_spb=false);
void stop();
private:
Camera& m_cam;
......@@ -151,14 +152,18 @@ class Camera
void getImageCount(unsigned int& img_count, bool only_lsw=false);
void getMissingExtStartPulses(int& missing_pulses);
void prepare();
void start();
void stop();
bool isRunning();
AcqSeq startAcqSeq();
bool needSeqTimMeasure();
void latchSeqTimValues(SeqTimValues& st);
void measureSeqTimValues(SeqTimValues& st, double timeout);
void measureSeqTimValues(SeqTimValues& st, double timeout = -1);
void setAutoSeqTimMeasure(bool auto_measure);
void getAutoSeqTimMeasure(bool& auto_measure);
void registerDeadTimeChangedCallback(DeadTimeChangedCallback& cb);
void unregisterDeadTimeChangedCallback(DeadTimeChangedCallback& cb);
......@@ -167,12 +172,15 @@ class Camera
void unregisterMaxImageSizeCallback(HwMaxImageSizeCallback& cb);
private:
friend class TimingCtrl;
static const double ResetLinkWaitTime;
static const double UpdateCcdStatusTime;
static const double MaxIdleWaitTime;
static const double MaxBusyRetryTime;
Espia::Dev& getEspiaDev();
Geometry& getGeometry();
void sync();
void syncRegs();
......@@ -195,6 +203,7 @@ class Camera
SerialLine m_ser_line;
Model m_model;
bool m_auto_seq_tim_measure;
AutoPtr<TimingCtrl> m_timing_ctrl;
AutoPtr<Geometry> m_geom;
TrigMode m_trig_mode;
......
......@@ -104,6 +104,11 @@ class Geometry : public HwMaxImageSizeCallbackGen
void registerDeadTimeChangedCallback(DeadTimeChangedCallback& cb);
void unregisterDeadTimeChangedCallback(DeadTimeChangedCallback& cb);
Flip getMirror();
Point getNbChan();
Size getCcdSize();
Size getChanSize();
protected:
virtual void setMaxImageSizeCallbackActive(bool cb_active);
......@@ -126,11 +131,7 @@ class Geometry : public HwMaxImageSizeCallbackGen
void setFlipMode(int flip_mode);
void getFlipMode(int& flip_mode);
Flip getMirror();
Point getNbChan();
Size getCcdSize();
Size getChanSize();
Flip getRoiInsideMirror();
Flip getRoiInsideMirror();
void writeChanRoi(const Roi& chan_roi);
void readChanRoi(Roi& chan_roi);
......
......@@ -54,6 +54,17 @@ class TimingCtrl
}
};
struct Config {
int config_hd;
int bin_vert;
int chan_mode;
int nb_lines_xfer;
int roi_enable, roi_fast, roi_kinetic;
int roi_line_begin, roi_line_width;
int shut_elec_select;
};
typedef std::map<Config, SeqTimValues> ConfigTimingMeasureMap;
TimingCtrl(Camera& cam);
virtual ~TimingCtrl();
......@@ -61,18 +72,25 @@ class TimingCtrl
void getTransferTime(double& xfer_time);
void getDeadTime(double& dead_time);
bool needSeqTimMeasure();
void latchSeqTimValues(SeqTimValues& st);
void measureSeqTimValues(SeqTimValues& st, double timeout);
void measureSeqTimValues(SeqTimValues& st, double timeout = -1);
protected:
void writeRegister(Reg reg, int val);
void readRegister (Reg reg, int& val);
void readFloatRegister(Reg reg, double& val);
Config getConfig();
Camera& m_cam;
Model& m_model;
ConfigTimingMeasureMap m_timing_measure_cache;
};
std::ostream& operator <<(std::ostream& os, const TimingCtrl::Config& config);
bool operator <(const TimingCtrl::Config& a, const TimingCtrl::Config& b);
} // namespace Frelon
......
......@@ -38,6 +38,7 @@ enum Reg {
ChanMode, TimeUnit, RoiEnable, RoiFast,
AntiBloom, BinVert, BinHorz, ConfigHD,
RoiKinetic, ShutEnable, HardTrigDisable,
NbLinesXfer, ShutElecSelect,
PixelFreq, LineFreq, FlipMode, IntCalib,
DisplayImage, AdcFloatDiode, AdcSignal,
DarkPixelCalib, DarkPixelMode, ChanControl, Mire,
......@@ -46,14 +47,17 @@ enum Reg {
Version, CompSerNb, Warn, LastWarn,
LineClockPer, PixelClockPer, FirstPHIVLen, PHIHSetupLen,
SingleVertXfer, SingleHorzXfer, AllVertXfer, AllHorzXfer,
ReadoutTime, TransferTime, CcdModesAvail, StatusSeqA,
ReadoutTime, TransferTime, CcdModesAvail,
StatusSeqA, StatusSeqB,
StatusAMTA, StatusAMTB, StatusAMTC, StatusAMTD,
StatusAMTE,
LookUpTable, ImagesPerEOF, WeightValDFl, WeightValSig,
SeqClockFreq, CamChar,
SeqTimRdOutH, SeqTimRdOutL, SeqTimTransferH,SeqTimTransferL,
SeqTimEShutH, SeqTimEShutL, SeqTimExposureH,SeqTimExposureL,
SeqTimFramePeriodH, SeqTimFramePeriodL,
SeqTimRdOutH, SeqTimRdOutL,
SeqTimTransferH, SeqTimTransferL,
SeqTimEShutH, SeqTimEShutL,
SeqTimExposureH, SeqTimExposureL,
SeqTimFramePeriodH, SeqTimFramePeriodL,
};
/*
typedef std::map<Reg, std::string> RegStrMapType;
......
......@@ -119,12 +119,16 @@ class Camera
void getImageCount(unsigned int& img_count /Out/, bool only_lsw=false);
void getMissingExtStartPulses(int& missing_pulses /Out/);
void prepare();
void start();
void stop();
bool isRunning();
bool needSeqTimMeasure();
void latchSeqTimValues(Frelon::SeqTimValues& st /Out/);
void measureSeqTimValues(Frelon::SeqTimValues& st /Out/, double timeout);
void measureSeqTimValues(Frelon::SeqTimValues& st /Out/, double timeout = -1);
void setAutoSeqTimMeasure(bool auto_measure);
void getAutoSeqTimMeasure(bool& auto_measure /Out/);
void registerDeadTimeChangedCallback(Frelon::DeadTimeChangedCallback& cb);
void unregisterDeadTimeChangedCallback(Frelon::DeadTimeChangedCallback& cb);
......
......@@ -126,7 +126,7 @@ static Reg CacheableRegCList[] = {
ChanMode, TimeUnit, RoiEnable, RoiFast,
RoiKinetic, BinVert, BinHorz, ConfigHD,
ShutEnable, HardTrigDisable, FlipMode, CompSerNb,
CcdModesAvail, ImagesPerEOF,
CcdModesAvail, ImagesPerEOF, NbLinesXfer, ShutElecSelect,
};
RegListType
lima::Frelon::CacheableRegList(C_LIST_ITERS(CacheableRegCList));
......
......@@ -20,6 +20,7 @@
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//###########################################################################
#include "FrelonCamera.h"
#include <iomanip>
using namespace lima;
using namespace lima::Frelon;
......@@ -67,10 +68,11 @@ void Camera::AcqSeq::stop()
m_cam.stop();
}
bool Camera::AcqSeq::wait(double timeout)
bool Camera::AcqSeq::wait(double timeout, bool use_ser_line, bool read_spb)
{
Status status = Wait;
return m_cam.waitStatus(status, StatusMask, timeout);
return m_cam.waitStatus(status, StatusMask, timeout,
use_ser_line, read_spb);
}
const double Camera::ResetLinkWaitTime = 5;
......@@ -79,7 +81,7 @@ const double Camera::MaxIdleWaitTime = 2.5;
const double Camera::MaxBusyRetryTime = 0.2; // 16 Mpixel image Aurora Xfer
Camera::Camera(Espia::SerialLine& espia_ser_line)
: m_ser_line(espia_ser_line)
: m_ser_line(espia_ser_line), m_auto_seq_tim_measure(true)
{
DEB_CONSTRUCTOR();
......@@ -265,6 +267,11 @@ Espia::Dev& Camera::getEspiaDev()
return dev;
}
Geometry& Camera::getGeometry()
{
return *m_geom;
}
void Camera::sendCmd(Cmd cmd)
{
DEB_MEMBER_FUNCT();
......@@ -688,6 +695,8 @@ void Camera::setTotalLatTime(double lat_time)
DEB_PARAM() << DEB_VAR1(lat_time);
double dead_time;
getDeadTime(dead_time);
if (dead_time < 0)
dead_time = 0;
double user_lat_time = max(0.0, lat_time - dead_time);
setUserLatTime(user_lat_time);
}
......@@ -699,6 +708,8 @@ void Camera::getTotalLatTime(double& lat_time)
getUserLatTime(user_lat_time);
double dead_time;
getDeadTime(dead_time);
if (dead_time < 0)
dead_time = 0;
lat_time = dead_time + user_lat_time;
DEB_RETURN() << DEB_VAR1(lat_time);
}
......@@ -891,6 +902,26 @@ void Camera::getMissingExtStartPulses(int& missing_pulses)
DEB_RETURN() << DEB_VAR1(missing_pulses);
}
void Camera::prepare()
{
DEB_MEMBER_FUNCT();
bool do_seq_tim_measure = (m_auto_seq_tim_measure &&
needSeqTimMeasure());
if (do_seq_tim_measure) {
DEB_ALWAYS() << "Measuring Frelon timing ...";
SeqTimValues st;
measureSeqTimValues(st);
double readout_ms = st.readout_time * 1e3;
double xfer_ms = st.transfer_time * 1e3;
ostringstream os;
int prec = os.precision();
DEB_ALWAYS() << fixed << setprecision(3)
<< DEB_VAR2(readout_ms, xfer_ms)
<< setprecision(prec);
}
}
void Camera::start()
{
DEB_MEMBER_FUNCT();
......@@ -991,6 +1022,12 @@ double Camera::getMaxIdleWaitTime()
return max_wait_time;
}
bool Camera::needSeqTimMeasure()
{
DEB_MEMBER_FUNCT();
return m_timing_ctrl->needSeqTimMeasure();
}
void Camera::latchSeqTimValues(SeqTimValues& st)
{
DEB_MEMBER_FUNCT();
......@@ -1001,6 +1038,7 @@ void Camera::measureSeqTimValues(SeqTimValues& st, double timeout)
{
DEB_MEMBER_FUNCT();
m_timing_ctrl->measureSeqTimValues(st, timeout);
m_geom->deadTimeChanged();
}
void Camera::registerDeadTimeChangedCallback(DeadTimeChangedCallback& cb)
......@@ -1015,6 +1053,19 @@ void Camera::unregisterDeadTimeChangedCallback(DeadTimeChangedCallback& cb)
m_geom->unregisterDeadTimeChangedCallback(cb);
}
void Camera::setAutoSeqTimMeasure(bool auto_measure)
{
DEB_MEMBER_FUNCT();
DEB_PARAM() << DEB_VAR1(auto_measure);
m_auto_seq_tim_measure = auto_measure;
}
void Camera::getAutoSeqTimMeasure(bool& auto_measure)
{
DEB_MEMBER_FUNCT();
auto_measure = m_auto_seq_tim_measure;
DEB_RETURN() << DEB_VAR1(auto_measure);
}
void Camera::registerMaxImageSizeCallback(HwMaxImageSizeCallback& cb)
{
......
......@@ -936,7 +936,7 @@ void Interface::resetDefaults()
m_cam.setRoiBinOffset(Point(0));
m_bin.setBin(Bin(1));
m_roi.setRoi(Roi());
Size image_size;
m_det_info.getMaxImageSize(image_size);
ImageType image_type;
......@@ -951,6 +951,7 @@ void Interface::resetDefaults()
void Interface::prepareAcq()
{
DEB_MEMBER_FUNCT();
m_cam.prepare();
}
void Interface::startAcq()
......
......@@ -57,8 +57,25 @@ SeqTimValues TimingCtrl::SeqTim::calcValues(const ValPairList& l)
return st;
}
#define ConfigSeqTimVal(c, b, m, r, t) \
{{c, b, m, 0, 0, 0, 0, 0, 0, 0}, {r / 1e6, t / 1e6, 0, 0, 0}}
typedef TimingCtrl::ConfigTimingMeasureMap::value_type ConfigSeqTimPair;
static ConfigSeqTimPair InitialTimingMeasureCacheCList[] = {
ConfigSeqTimVal(0, 1, 9, 99072, 0),
ConfigSeqTimVal(0, 2, 9, 50880, 0),
ConfigSeqTimVal(1, 1, 9, 50880, 0),
ConfigSeqTimVal(1, 2, 9, 26784, 0),
ConfigSeqTimVal(0, 1, 10, 49536, 1394),
ConfigSeqTimVal(0, 2, 10, 25440, 1394),
ConfigSeqTimVal(1, 1, 10, 25440, 1369),
ConfigSeqTimVal(1, 2, 10, 13392, 1369),
};
TimingCtrl::TimingCtrl(Camera& cam)
: m_cam(cam), m_model(cam.getModel())
: m_cam(cam), m_model(cam.getModel()),
m_timing_measure_cache(C_LIST_ITERS(InitialTimingMeasureCacheCList))
{
DEB_CONSTRUCTOR();
}
......@@ -87,7 +104,14 @@ void TimingCtrl::getReadoutTime(double& readout_time)
{
DEB_MEMBER_FUNCT();
if (m_model.has(Model::SeqTim)) {
readout_time = 100e-3;
if (needSeqTimMeasure()) {
DEB_ERROR() << "Camera needs SeqTim measurement";
readout_time = -1;
} else {
Config config = getConfig();
const SeqTimValues& st = m_timing_measure_cache[config];
readout_time = st.readout_time;
}
} else if (m_model.has(Model::TimeCalc)) {
readFloatRegister(ReadoutTime, readout_time);
readout_time *= 1e-6;
......@@ -102,7 +126,14 @@ void TimingCtrl::getTransferTime(double& xfer_time)
{
DEB_MEMBER_FUNCT();
if (m_model.has(Model::SeqTim)) {
xfer_time = 100e-3;
if (needSeqTimMeasure()) {
DEB_ERROR() << "Camera needs SeqTim measurement";
xfer_time = -1;
} else {
Config config = getConfig();
const SeqTimValues& st = m_timing_measure_cache[config];
xfer_time = st.transfer_time;
}
} else if (m_model.has(Model::TimeCalc)) {
readFloatRegister(TransferTime, xfer_time);
xfer_time *= 1e-6;
......@@ -122,6 +153,50 @@ void TimingCtrl::getDeadTime(double& dead_time)
DEB_RETURN() << DEB_VAR1(dead_time);
}
TimingCtrl::Config TimingCtrl::getConfig()
{
DEB_MEMBER_FUNCT();
if (!m_model.has(Model::SeqTim))
THROW_HW_ERROR(NotSupported) << "Camera does not have "
<< "sequencer timing feature";
Config config;
readRegister(ConfigHD, config.config_hd);
readRegister(BinVert, config.bin_vert);
readRegister(ChanMode, config.chan_mode);
readRegister(NbLinesXfer, config.nb_lines_xfer);
readRegister(RoiEnable, config.roi_enable);
readRegister(RoiFast, config.roi_fast);
readRegister(RoiKinetic, config.roi_kinetic);
if (config.roi_enable || config.roi_kinetic) {
readRegister(RoiLineBegin, config.roi_line_begin);
readRegister(RoiLineWidth, config.roi_line_width);
} else {
config.roi_line_begin = 0;
config.roi_line_width = 0;
}
readRegister(ShutElecSelect, config.shut_elec_select);
DEB_RETURN() << DEB_VAR1(config);
return config;
}
bool TimingCtrl::needSeqTimMeasure()
{
DEB_MEMBER_FUNCT();
bool need_measure;
if (m_model.has(Model::SeqTim)) {
Config config = getConfig();
DEB_TRACE() << DEB_VAR1(config);
typedef ConfigTimingMeasureMap::const_iterator It;
It it, end = m_timing_measure_cache.end();
need_measure = (m_timing_measure_cache.find(config) == end);
} else {
need_measure = false;
}
DEB_RETURN() << DEB_VAR1(need_measure);
return need_measure;
}
void TimingCtrl::latchSeqTimValues(SeqTimValues& st)
{
DEB_MEMBER_FUNCT();
......@@ -145,9 +220,13 @@ void TimingCtrl::latchSeqTimValues(SeqTimValues& st)
void TimingCtrl::measureSeqTimValues(SeqTimValues& st, double timeout)
{
DEB_MEMBER_FUNCT();
if (!m_model.has(Model::SeqTim))
THROW_HW_ERROR(NotSupported) << "Camera does not have "
<< "sequencer timing feature";
DEB_PARAM() << DEB_VAR1(timeout);
if (timeout == -1)
timeout = 5.0;
Config config = getConfig();
ExtSync ext_sync_ena;
m_cam.getExtSyncEnable(ext_sync_ena);
if (ext_sync_ena != ExtSyncNone)
......@@ -155,12 +234,62 @@ void TimingCtrl::measureSeqTimValues(SeqTimValues& st, double timeout)
Camera::TempRegVal n = m_cam.getTempRegVal(NbFrames, 3);
Camera::TempRegVal i = m_cam.getTempRegVal(ExpTime, 1);
Camera::TempRegVal t = m_cam.getTempRegVal(LatencyTime, 0);
Camera::TempRegVal u = m_cam.getTempRegVal(ShutEnable, 0);
Camera::AcqSeq acq = m_cam.startAcqSeq();
if (!acq.wait(timeout))
if (!acq.wait(timeout, true, true))
THROW_HW_ERROR(Error) << "Camera not ready after "
<< timeout << " sec";
latchSeqTimValues(st);
DEB_TRACE() << DEB_VAR2(config, st);
m_timing_measure_cache[config] = st;
DEB_RETURN() << DEB_VAR1(st);
}
std::ostream& lima::Frelon::operator <<(std::ostream& os,
const TimingCtrl::Config& config)
{
return os << '<'
<< config.config_hd << ", "
<< config.bin_vert << ", "
<< config.chan_mode << ", "
<< config.nb_lines_xfer << ", "
<< config.roi_enable << ", "
<< config.roi_fast << ", "
<< config.roi_kinetic << ", "
<< config.roi_line_begin << ", "
<< config.roi_line_width << ", "
<< config.shut_elec_select << '>';
}
bool lima::Frelon::operator <(const TimingCtrl::Config& a,
const TimingCtrl::Config& b)
{
#define check_val(a, b) \
do { \
if ((a) < (b)) \
return true; \
else if ((a) > (b)) \
return false; \
} while (0)
#define check(x) check_val(a.x, b.x)
check(config_hd);
check(bin_vert);
check(chan_mode);
check(nb_lines_xfer);
check(roi_enable);
check(roi_fast);
check(roi_kinetic);
check(roi_line_begin);
check(roi_line_width);
check(shut_elec_select);
#undef check
#undef check_val
return false;
}
......@@ -197,6 +197,22 @@ class Frelon(PyTango.Device_4Impl):
reset_trace_log = ser_line.getResetTraceLog()
attr.set_value(reset_trace_log)
def read_readout_time(self,attr):
cam = _FrelonAcq.getFrelonCamera()
if cam.needSeqTimMeasure():
raise Core.Exception('Camera needs SeqTim measurement')
attr.set_value(cam.getReadoutTime())
def read_transfer_time(self,attr):
cam = _FrelonAcq.getFrelonCamera()
if cam.needSeqTimMeasure():
raise Core.Exception('Camera needs SeqTim measurement')
attr.set_value(cam.getTransferTime())
def read_need_seq_tim_measure(self,attr):
cam = _FrelonAcq.getFrelonCamera()
attr.set_value(cam.needSeqTimMeasure())
class FrelonClass(PyTango.DeviceClass):
......@@ -290,6 +306,14 @@ class FrelonClass(PyTango.DeviceClass):
[[PyTango.DevFloat,
PyTango.SCALAR,
PyTango.READ]],
'need_seq_tim_measure' :
[[PyTango.DevBoolean,
PyTango.SCALAR,
PyTango.READ]],
'auto_seq_tim_measure' :
[[PyTango.DevBoolean,
PyTango.SCALAR,
PyTango.READ_WRITE]],
'image_count' :
[[PyTango.DevLong,
PyTango.SCALAR,
......
import sys
from Lima import Core, Frelon
from collections import namedtuple
acq = None
cam = None
ct_control = None
ct_image = None
RoiConfig = namedtuple('RoiConfig',
['enable', 'fast', 'kinetic', 'line_begin', 'line_width'],
defaults=[0, 0, 0, 0, 1])
ImageConfig = namedtuple('ImageConfig', ['config_hd', 'bin_vert', 'chan_mode'])
def init(frelon_acq_args):
global acq, cam, ct_control, ct_image
acq = Frelon.FrelonAcq(*frelon_acq_args)
cam = acq.getFrelonCamera()
ct_control = acq.getGlobalControl()
ct_image = ct_control.image()
def toUSec(x):
return int(x * 1e6)
def measure(check_expected=False):
expected = None
needed_seqtim_measure = False
if check_expected:
needed_seqtim_measure = cam.needSeqTimMeasure()
if needed_seqtim_measure:
expected = (0, 0)
else:
expected = (toUSec(cam.getReadoutTime()),
toUSec(cam.getTransferTime()))
image_config = getImageConfig()
st = cam.measureSeqTimValues()
readout_us, transfer_us = toUSec(st.readout_time), toUSec(st.transfer_time)
ok = not expected or (readout_us, transfer_us) == expected
if ok:
status = 'OK'
else:
status = ('Camera needed SeqTim measurement'
if needed_seqtim_measure
else f'Mismatch: expected {expected}')
print(f'Config {image_config}: '
f'readout_time={readout_us:8d} us, '
f'transfer_time={transfer_us:8d} us: {status}')
def setRoi(roi_config):
cam.writeRegister(Frelon.RoiEnable, roi_config.enable)
cam.writeRegister(Frelon.RoiFast, roi_config.fast)
cam.writeRegister(Frelon.RoiKinetic, roi_config.kinetic)
cam.writeRegister(Frelon.RoiLineBegin, roi_config.line_begin)
cam.writeRegister(Frelon.RoiLineWidth, roi_config.line_width)
def setImageConfig(image_config):
cam.writeRegister(Frelon.ConfigHD, image_config.config_hd)
cam.writeRegister(Frelon.BinVert, image_config.bin_vert)
cam.writeRegister(Frelon.ChanMode, image_config.chan_mode)
cam.writeRegister(Frelon.NbLinesXfer, 0)
cam.writeRegister(Frelon.ShutElecSelect, 0)
def getImageConfig():
return ImageConfig(cam.readRegister(Frelon.ConfigHD),
cam.readRegister(Frelon.BinVert),
cam.readRegister(Frelon.ChanMode))</