Commit 85e51a3a authored by Federico Vaga's avatar Federico Vaga

Merge tag 'v4.0.0' into develop

4.0.0 - 2021-09-10
==================
Added
-----
- the adc-acq tool uses VMALLOC by default
- tst: regression test for spec45t digital noise

Changed
-------
- the minimum required version for the fmc-adc-100m driver is v6.0.0
- the library converts offset microVolts to fmc-adc-100m raw values
- tst: improve tests
parents 1e40c00a 764f2799
......@@ -39,6 +39,11 @@ documentation:
stage: build
image:
name: gitlab-registry.cern.ch/coht/common-containers/documentation:latest
before_script:
- git clone -b v1.4.0 --depth 1 https://ohwr.org/project/zio.git ~/git/zio
- export ZIO=~/git/zio
- git clone -b v5.0.0 --depth 1 https://gitlab.cern.ch/coht/fmc-adc-100m14b4cha.git ~/git/fmc-adc-100m14b4cha
- export FMCADC100M=~/git/fmc-adc-100m14b4cha/software
script:
- make -C lib
- LD_LIBRARY_PATH=$PWD/lib make -C doc html
......
......@@ -6,6 +6,19 @@
Changelog
=========
4.0.0 - 2021-09-10
==================
Added
-----
- the adc-acq tool uses VMALLOC by default
- tst: regression test for spec45t digital noise
Changed
-------
- the minimum required version for the fmc-adc-100m driver is v6.0.0
- the library converts offset microVolts to fmc-adc-100m raw values
- tst: improve tests
3.0.0 - 2020-12-10
==================
Added
......
......@@ -12,6 +12,9 @@ IGNORE_CPU_SUFFIX := y
REPO_PARENT ?=
-include $(REPO_PARENT)/parent_common.mk
DESTDIR ?= /usr/local
ZIO ?= $(HOME)/git/zio
ZIO_ABS ?= $(abspath $(ZIO) )
......@@ -19,7 +22,12 @@ GIT_VERSION := $(shell git describe --dirty --long --tags)
ZIO_GIT_VERSION := $(shell cd $(ZIO_ABS); git describe --dirty --long --tags)
FMCADC_100MS_4CH_14BIT_GIT_VERSION := $(shell cd $(FMCADC100M); git describe --dirty --long --tags)
SO_VERSION_XYZ := $(shell echo $(GIT_VERSION) | grep -o -E "[0-9]+\.[0-9]+\.[0-9]")
SO_VERSION_X := $(shell echo $(SO_VERSION_XYZ) | cut -d "." -f 1)
LIBS = libadc.so
LIBS_XYZ = $(LIBS).$(SO_VERSION_XYZ)
LIB = libadc.a
LOBJ := route.o
LOBJ += init.o
......@@ -45,7 +53,7 @@ CC ?= $(CROSS_COMPILE)gcc
CPPCHECK ?= cppcheck
FLAWFINDER ?= flawfinder
all: $(LIB) $(LIBS)
all: $(LIB) $(LIBS_XYZ)
%: %.c $(LIB)
......@@ -54,7 +62,7 @@ all: $(LIB) $(LIBS)
$(LIB): $(LOBJ)
$(AR) r $@ $^
$(LIBS): $(LIB)
$(LIBS_XYZ): $(LIB)
$(CC) -shared -o $@ -Wl,--whole-archive,-soname,$@ $^ -Wl,--no-whole-archive
cppcheck:
......@@ -64,11 +72,17 @@ flawfinder:
$(FLAWFINDER) -SQDC --error-level=4 .
clean:
rm -f $(LIB) $(LIBS) .depend *.o *~
rm -f $(LIB) $(LIBS_XYZ) .depend *.o *~
.depend: Makefile $(wildcard *.c *.h ../*.h)
$(CC) $(CFLAGS) -M $(LOBJ:.o=.c) -o $@
install:
install: $(LIBS_XYZ) $(LIB)
install -d $(DESTDIR)/lib
install -m 644 $(LIB) $(DESTDIR)/lib
install -m 755 $(LIBS_XYZ) $(DESTDIR)/lib
ln -sf $(LIBS_XYZ) $(DESTDIR)/lib/$(LIBS).$(SO_VERSION_X)
ln -sf $(LIBS).$(SO_VERSION_X) $(DESTDIR)/lib/$(LIBS)
-include .depend
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright CERN 2021
/*It depends on the header fmcadc-lib.h. Include this file after fmcadc-lib.h*/
#define fmcadc_dev adc_dev
#define fmcadc_buffer adc_buffer
#define fmcadc_timestamp adc_timestamp
#define fmcadc_conf adc_conf
#define __fmcadc_dev_zio __adc_dev_zio
#define fmcadc_board_type adc_board_type
#define fmcadc_operations adc_operations
#define fmcadc_gid adc_gid
#define __fmcadc_genfake_conf __adc_genfake_conf
#define __fmcadc_dev_genfake __adc_dev_genfake
#define __FMCADC_ERRNO_START __ADC_ERRNO_START
#define FMCADC_ENOP ADC_ENOP
#define FMCADC_ENOCAP ADC_ENOCAP
#define FMCADC_ENOCFG ADC_ENOCFG
#define FMCADC_ENOGET ADC_ENOGET
#define FMCADC_ENOSET ADC_ENOSET
#define FMCADC_ENOCHAN ADC_ENOCHAN
#define FMCADC_ENOMASK ADC_ENOMASK
#define FMCADC_EDISABLED ADC_EDISABLED
#define FMCADC_CONF_TRG_SOURCE ADC_CONF_TRG_SOURCE
#define FMCADC_CONF_TRG_SOURCE_CHAN ADC_CONF_TRG_SOURCE_CHAN
#define FMCADC_CONF_TRG_THRESHOLD ADC_CONF_TRG_THRESHOLD
#define FMCADC_CONF_TRG_POLARITY ADC_CONF_TRG_POLARITY
#define FMCADC_CONF_TRG_DELAY ADC_CONF_TRG_DELAY
#define FMCADC_CONF_TRG_THRESHOLD_FILTER ADC_CONF_TRG_THRESHOLD_FILTER
#define __FMCADC_CONF_TRG_ATTRIBUTE_LAST_INDEX __ADC_CONF_TRG_ATTRIBUTE_LAST_INDEX
#define FMCADC_CONF_ACQ_N_SHOTS ADC_CONF_ACQ_N_SHOTS
#define FMCADC_CONF_ACQ_POST_SAMP ADC_CONF_ACQ_POST_SAMP
#define FMCADC_CONF_ACQ_PRE_SAMP ADC_CONF_ACQ_PRE_SAMP
#define FMCADC_CONF_ACQ_DECIMATION ADC_CONF_ACQ_DECIMATION
#define FMCADC_CONF_ACQ_FREQ_HZ ADC_CONF_ACQ_FREQ_HZ
#define FMCADC_CONF_ACQ_N_BITS ADC_CONF_ACQ_N_BITS
#define __FMCADC_CONF_ACQ_ATTRIBUTE_LAST_INDEX __ADC_CONF_ACQ_ATTRIBUTE_LAST_INDEX
#define FMCADC_CONF_CHN_RANGE ADC_CONF_CHN_RANGE
#define FMCADC_CONF_CHN_TERMINATION ADC_CONF_CHN_TERMINATION
#define FMCADC_CONF_CHN_OFFSET ADC_CONF_CHN_OFFSET
#define FMCADC_CONF_CHN_SATURATION ADC_CONF_CHN_SATURATION
#define __FMCADC_CONF_CHN_ATTRIBUTE_LAST_INDEX __ADC_CONF_CHN_ATTRIBUTE_LAST_INDEX
#define FMCADC_CONF_BRD_STATUS ADC_CONF_BRD_STATUS
#define FMCADC_CONF_BRD_MAX_FREQ_HZ ADC_CONF_BRD_MAX_FREQ_HZ
#define FMCADC_CONF_BRD_MIN_FREQ_HZ ADC_CONF_BRD_MIN_FREQ_HZ
#define FMCADC_CONF_BRD_STATE_MACHINE_STATUS ADC_CONF_BRD_STATE_MACHINE_STATUS
#define FMCADC_CONF_BRD_N_CHAN ADC_CONF_BRD_N_CHAN
#define FMCADC_CONF_UTC_TIMING_BASE_S ADC_CONF_UTC_TIMING_BASE_S
#define FMCADC_CONF_UTC_TIMING_BASE_T ADC_CONF_UTC_TIMING_BASE_T
#define FMCADC_CONF_UTC_TIMING_BASE_B ADC_CONF_UTC_TIMING_BASE_B
#define __FMCADC_CONF_BRD_ATTRIBUTE_LAST_INDEX __ADC_CONF_BRD_ATTRIBUTE_LAST_INDEX
#define FMCADC_CONF_TYPE_TRG ADC_CONF_TYPE_TRG
#define FMCADC_CONF_TYPE_ACQ ADC_CONF_TYPE_ACQ
#define FMCADC_CONF_TYPE_CHN ADC_CONF_TYPE_CHN
#define FMCADC_CONT_TYPE_BRD ADC_CONT_TYPE_BRD
#define __FMCADC_CONF_TYPE_LAST_INDEX __ADC_CONF_TYPE_LAST_INDEX
#define __FMCADC_CONF_LEN __ADC_CONF_LEN
#define FMCADC_F_USERMASK ADC_F_USERMASK //found as FMCSDC_F_USERMASK was it a mistake?
#define FMCADC_F_FLUSH ADC_F_FLUSH
#define FMCADC_F_VERBOSE ADC_F_VERBOSE
static inline int fmcadc_init(void)
{
return adc_init();
}
static inline void fmcadc_exit(void)
{
return adc_exit();
}
static inline char *fmcadc_strerror(int errnum)
{
return adc_strerror(errnum);
}
static inline char *fmcadc_get_driver_type(struct fmcadc_dev *dev)
{
return adc_get_driver_type(dev);
}
static inline struct fmcadc_dev *fmcadc_open(char *name, unsigned int dev_id,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags)
{
return adc_open(name, dev_id, totalsamples, nbuffer, flags);
}
static inline struct fmcadc_dev *fmcadc_open_by_lun(char *name, int lun,
unsigned long totalsamples,
unsigned int nbuffer,
unsigned long flags)
{
return adc_open_by_lun(name, lun, totalsamples, nbuffer, flags);
}
static inline int fmcadc_close(struct fmcadc_dev *dev)
{
return adc_close(dev);
}
static inline int fmcadc_acq_start(struct fmcadc_dev *dev,
unsigned int flags,
struct timeval *timeout)
{
return adc_acq_start(dev, flags, timeout);
}
static inline int fmcadc_acq_poll(struct fmcadc_dev *dev, unsigned int flags,
struct timeval *timeout)
{
return adc_acq_poll(dev, flags, timeout);
}
static inline int fmcadc_acq_stop(struct fmcadc_dev *dev, unsigned int flags)
{
return adc_acq_stop(dev, flags);
}
static inline int fmcadc_reset_conf(struct fmcadc_dev *dev, unsigned int flags,
struct adc_conf *conf)
{
return adc_reset_conf(dev, flags, conf);
}
static inline int fmcadc_apply_config(struct fmcadc_dev *dev, unsigned int flags,
struct adc_conf *conf)
{
return adc_apply_config(dev, flags, conf);
}
static inline int fmcadc_retrieve_config(struct fmcadc_dev *dev,
struct adc_conf *conf)
{
return adc_retrieve_config(dev, conf);
}
static inline int fmcadc_set_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
return adc_set_param(dev, name, sptr, iptr);
}
static inline int fmcadc_get_param(struct fmcadc_dev *dev, char *name,
char *sptr, int *iptr)
{
return adc_get_param(dev, name, sptr, iptr);
}
static inline struct adc_buffer *fmcadc_request_buffer(struct fmcadc_dev *dev,
int nsamples,
void *(*alloc_fn)(size_t),
unsigned int flags)
{
return adc_request_buffer(dev, nsamples, alloc_fn, flags);
}
static inline int fmcadc_fill_buffer(struct fmcadc_dev *dev,
struct adc_buffer *buf,
unsigned int flags,
struct timeval *timeout)
{
return adc_fill_buffer(dev, buf, flags, timeout);
}
static inline struct fmcadc_timestamp *fmcadc_tstamp_buffer(struct adc_buffer *buf,
struct fmcadc_timestamp *ts)
{
return adc_tstamp_buffer(buf, ts);
}
static inline int fmcadc_release_buffer(struct fmcadc_dev *dev,
struct adc_buffer *buf,
void (*free_fn)(void *))
{
return adc_release_buffer(dev, buf, free_fn);
}
static inline void fmcadc_set_conf_mask(struct adc_conf *conf,
unsigned int conf_index)
{
return adc_set_conf_mask(conf, conf_index);
}
static inline void fmcadc_set_conf(struct adc_conf *conf,
unsigned int conf_index, uint32_t val)
{
return adc_set_conf(conf, conf_index, val);
}
static inline int fmcadc_get_conf(struct adc_conf *conf,
unsigned int conf_index,
uint32_t *val)
{
return adc_get_conf(conf, conf_index, val);
}
......@@ -34,6 +34,7 @@ extern "C" {
#define ADC_OFF_AC_RESTORE_S 1037
#define ADC_OFF_AC_RESTORE_R 1038
#define ADC_ENOFLAGS 1039
#define ADC_EDRVVERSION 1040
/**
* Opaque type. any instance of this should be used as token
......@@ -186,7 +187,8 @@ enum adc_configuration_acquisition {
enum adc_configuration_channel {
ADC_CONF_CHN_RANGE = 0, /**< Volt range */
ADC_CONF_CHN_TERMINATION, /**< channel Termination */
ADC_CONF_CHN_OFFSET, /**< signal offset */
ADC_CONF_CHN_OFFSET, /**< signal offset [uV]. Depending on board
this value could be approximated */
ADC_CONF_CHN_SATURATION, /**< saturation value */
ADC_CONF_CHN_GAIN, /**< signal gain */
__ADC_CONF_CHN_ATTRIBUTE_LAST_INDEX, /**< It represents the the last
......
......@@ -33,6 +33,10 @@
#define ADC_CONF_GET 0
#define ADC_CONF_SET 1
/**
* This is for internal use. It uses the offset raw value
*/
#define __ADC_CONF_CHN_OFFSET_ZERO 200
......@@ -80,9 +84,9 @@ static typeof(adc_get_param) *adc_param[] = {
* ADC internal data
*/
struct adc_100m14b4cha {
/**< keep track of all zero-offset values for each range
/**< keep track of all zero-offset values (raw values) for each range
(100mV 1V 10V) */
int32_t offset_zero[FA100M14B4C_NCHAN][ADC_CONF_100M14B4CHA_CHN_RANGE_N];
uint32_t offset_zero[FA100M14B4C_NCHAN][ADC_CONF_100M14B4CHA_CHN_RANGE_N];
};
static int adc_100m14b4cha_offset_zero_set(struct adc_dev *dev);
......@@ -140,6 +144,7 @@ static int adc_100m14b4cha_buffer_resize(struct adc_dev *dev,
}
#define BUF_SIZE 128
#define FMCADC_100MS_4CH_14BIT_MIN_VERSION 6
static bool adc_100m14b4cha_is_driver_compatible(void)
{
int fd;
......@@ -159,6 +164,9 @@ static bool adc_100m14b4cha_is_driver_compatible(void)
if (ret != 2)
goto out;
if (maj < FMCADC_100MS_4CH_14BIT_MIN_VERSION)
goto out;
ret = sscanf(FMCADC_100MS_4CH_14BIT_GIT_VERSION, "v%u.%u",
&ref_maj, &ref_min);
if (ret != 2)
......@@ -177,6 +185,16 @@ out:
}
#undef BUF_SIZE
static void adc_100m14b4cha_init_data(struct adc_100m14b4cha *adcdata)
{
int i, k;
/* zero-offset 0.000V corresponds to 0x8000 */
for (i = 0; i < FA100M14B4C_NCHAN; ++i)
for (k = 0; k < ADC_CONF_100M14B4CHA_CHN_RANGE_N; ++k)
adcdata->offset_zero[i][k] = 0x8000;
}
/**
* @copydoc adc_open
* @param[in] b the board type description
......@@ -198,7 +216,7 @@ static struct adc_dev *adc_100m14b4cha_open(const struct adc_board_type *b,
int err, bits;
if (!adc_100m14b4cha_is_driver_compatible()) {
errno = EPERM;
errno = ADC_EDRVVERSION;
return NULL;
}
......@@ -220,8 +238,7 @@ static struct adc_dev *adc_100m14b4cha_open(const struct adc_board_type *b,
fa->priv = malloc(sizeof(struct adc_100m14b4cha));
if (!fa->priv)
return NULL;
memset(fa->priv, 0, sizeof(struct adc_100m14b4cha));
adc_100m14b4cha_init_data(fa->priv);
err = adc_100m14b4cha_offset_zero_set(dev);
if (err)
goto err;
......@@ -448,6 +465,23 @@ static int adc_100m14b4cha_config_acq(struct adc_dev *adc,
}
}
/**
* FMC-ADC-100M14B4CH offset volt reference
*/
#define ADC_100M14B4CHA_DAC_VREF_uV 5000000LL
/**
* Convert offset value from microVolt to raw
* @param[in] offset value in micro Volt
*
* @return offset raw value
*/
static uint32_t __offset_uv_to_raw(uint32_t value_uv)
{
int32_t value_uv_s = (int32_t)value_uv;
return (0x8000LL * (value_uv_s + ADC_100M14B4CHA_DAC_VREF_uV))/ADC_100M14B4CHA_DAC_VREF_uV;
}
#define ADC_100M14B4CHA_MAX_CHN_SRC FA100M14B4C_NCHAN
/**
......@@ -486,6 +520,7 @@ static int adc_100m14b4cha_config_chn(struct adc_dev *adc,
case ADC_CONF_CHN_OFFSET:
snprintf(path, sizeof(path), "cset%u/ch%u-offset",
fa->cset, source);
*value = __offset_uv_to_raw(*value);
break;
case __ADC_CONF_CHN_OFFSET_ZERO:
snprintf(path, sizeof(path), "cset%u/ch%u-offset-zero",
......@@ -1113,7 +1148,7 @@ static int __cfg_offac_apply(struct adc_dev *dev,
unsigned long flags)
{
int err, trgsrc = FA100M14B4C_TRG_SRC_SW, i;
uint32_t offset = 0;
uint32_t offset = 0x8000;
/* zero-offset needs to be cleard always */
for (i = 0; i < FA100M14B4C_NCHAN; ++i) {
......@@ -1139,46 +1174,6 @@ static int __cfg_offac_apply(struct adc_dev *dev,
return 0;
}
/**
* It converts an hardware value to uV
* @param[in] val value to be converted (hardware format)
* @param[in] range switch configuration read from the hardware
*
* @return uV value
*/
static int32_t __convert_hw_to_uv(int32_t val, unsigned int range)
{
int32_t result_int, result_frac, range_uV, factor;
switch (range) {
case ADC_CONF_100M14B4CHA_CHN_RANGE_10V_CAL:
case ADC_CONF_100M14B4CHA_CHN_RANGE_10V:
/* 10V */
range_uV = 10000000;
break;
case ADC_CONF_100M14B4CHA_CHN_RANGE_100mV_CAL:
case ADC_CONF_100M14B4CHA_CHN_RANGE_100mV:
/* 100mV */
range_uV = 100000;
break;
case ADC_CONF_100M14B4CHA_CHN_RANGE_1V_CAL:
case ADC_CONF_100M14B4CHA_CHN_RANGE_1V:
case ADC_CONF_100M14B4CHA_CHN_RANGE_OPEN_DRAIN:
/* 1V */
range_uV = 1000000;
break;
default:
fprintf(stderr, "Invalid switch configuration 0x%x\n", range);
return 0;
}
factor = (range_uV / BIT(14));
result_int = ((val >> 2) * factor);
result_frac = ((val & 0x3) * 10) * (factor / 1000);
return result_int + result_frac;
}
static int __offz_rng_idx_get(enum adc_configuration_100m14b4cha_channel_range range)
{
int idx = 0;
......@@ -1298,7 +1293,6 @@ static int adc_100m14b4cha_offac_sw_avg_man(struct adc_dev *dev,
for (i = 0; i < FA100M14B4C_NCHAN; ++i) {
int range = __offz_rng_idx_get(ranges[i]);
offset[i] = __convert_hw_to_uv(offset[i], ranges[i]);
adcdata->offset_zero[i][range] = offset[i];
}
......@@ -1351,10 +1345,8 @@ static int adc_100m14b4cha_offac_sw_avg_auto(struct adc_dev *dev,
* to the DAC. For this reason we do not need to revert
* that value: the offset sign measured it is fine
*/
for (k = 0; k < FA100M14B4C_NCHAN; ++k) {
offset[k] = __convert_hw_to_uv(offset[k], range);
for (k = 0; k < FA100M14B4C_NCHAN; ++k)
adcdata->offset_zero[k][i] = offset[k];
}
}
out:
......
......@@ -36,6 +36,7 @@ static struct adc_errors {
{ADC_OFF_AC_RESTORE_S, "Offset auto-clear: cannot store configuration"},
{ADC_OFF_AC_RESTORE_R, "Offset auto-clear: cannot restore configuration"},
{ADC_ENOFLAGS, "Unsupported flags"},
{ADC_EDRVVERSION, "Incompatible driver version"},
{ 0, }
};
......
......@@ -75,8 +75,9 @@ class TestAdcAcquisitionPattern(object):
last_ts = buf.contents.tstamp
if prev_ts is not None:
assert last_ts.to_ns_approx() > prev_ts.to_ns_approx()
assert last_ts.to_ns_approx() - prev_ts.to_ns_approx() > (sleep_time - 0.001) * 1e9
assert last_ts.to_ns_approx() - prev_ts.to_ns_approx() < (sleep_time + 0.001) * 1e9
delta = last_ts.to_ns_approx() - prev_ts.to_ns_approx()
assert delta > (sleep_time - 0.005) * 1e9
assert delta < (sleep_time + 0.005) * 1e9
prev_ts = last_ts
for chan in range(4):
......
......@@ -80,7 +80,7 @@ class TestAdcGetterSetterChannel(object):
adc100m14b4cha.retrieve_config(conf_rb)
assert conf.value_get(PyAdcConf.ADC_CONF_CHN_TERMINATION) == conf_rb.value_get(PyAdcConf.ADC_CONF_CHN_TERMINATION)
@pytest.mark.parametrize("offset", range(-5000000, 5000000 + 1, 100000))
@pytest.mark.parametrize("offset", range(-5000000, 4999999 + 1, 100000))
@pytest.mark.parametrize("channel", range(4))
def test_adc_offset(self, adc100m14b4cha, offset, channel):
"""Test that we can read/write offset on all channels"""
......@@ -94,7 +94,7 @@ class TestAdcGetterSetterChannel(object):
adc100m14b4cha.retrieve_config(conf_rb)
assert conf.value_get(PyAdcConf.ADC_CONF_CHN_OFFSET) == conf_rb.value_get(PyAdcConf.ADC_CONF_CHN_OFFSET)
@pytest.mark.parametrize("offset", [-5000001, 5000001])
@pytest.mark.parametrize("offset", [-5000001, 5000000])
@pytest.mark.parametrize("channel", range(4))
def test_adc_offset_invalid(self, adc100m14b4cha, offset, channel):
"""Test that we can read/write offset on all channels"""
......
......@@ -13,24 +13,31 @@ import matplotlib.pyplot as plt
def adc_simple_triangle(adc_simple):
yield adc_simple
def digital_noise_plot(raw_data, index, chan_err):
def digital_noise_plot(raw_data, index, chan_err, vref):
# convert signed data to mV (numpy array)
plot_data = np.array(raw_data)
# plot and store
f1, a1 = plt.subplots()
if vref == PyFmcAdc100m14b4ch.ADC_CONF_100M14B4CHA_CHN_RANGE_1V:
bit_scale = 0.5 / (1 << 15)
elif vref == PyFmcAdc100m14b4ch.ADC_CONF_100M14B4CHA_CHN_RANGE_10V:
bit_scale = 5.0 / (1 << 15)
else:
bit_scale = 1 # raw
volt_data = [data * bit_scale for data in raw_data]
for ch in range(0,4):
a1.plot(raw_data[ch::4], label='CH'+str(ch + 1) + (" WARN" if ch == chan_err else ""))
a1.plot(volt_data[ch::4], label='CH'+str(ch + 1) + (" WARN" if ch == chan_err else ""))
a1.set_xlabel('sample number')
a1.set_xticklabels(range(index, index + int(len(raw_data) / 4)))
a1.set_ylabel('raw value')
a1.set_ylabel('Volt')
a1.legend()
f1.savefig("/tmp/digital_noise_plot.png", dpi=300)
plt.show()
def digital_noise_report(buf, index, chan_err):
def digital_noise_report(buf, index, chan_err, vref):
message = "Unexpected value on channel {:d}\n".format(chan_err)
if index > 1:
for chan in range(4):
......@@ -44,26 +51,24 @@ def digital_noise_report(buf, index, chan_err):
samp,
prev,
samp)
subset = buf.contents.data[index * 4:(index+100) * 4]
subset = buf.contents.data[:buf.contents.nsamples * 4]
message += str(subset)
digital_noise_plot(subset, index, chan_err)
digital_noise_plot(subset, index, chan_err, vref)
return message
class TestAdcRegressions(object):
@pytest.mark.parametrize("vref", [PyFmcAdc100m14b4ch.ADC_CONF_100M14B4CHA_CHN_RANGE_100mV,
#PyFmcAdc100m14b4ch.ADC_CONF_100M14B4CHA_CHN_RANGE_1V,
#PyFmcAdc100m14b4ch.ADC_CONF_100M14B4CHA_CHN_RANGE_10V,
@pytest.mark.parametrize("vref", [PyFmcAdc100m14b4ch.ADC_CONF_100M14B4CHA_CHN_RANGE_1V,
PyFmcAdc100m14b4ch.ADC_CONF_100M14B4CHA_CHN_RANGE_10V,
])
@pytest.mark.parametrize("termination", [0, 1])
@pytest.mark.parametrize("nshots", [1, 10])
@pytest.mark.parametrize("post_samples", [2045, ])
@pytest.mark.parametrize("nshots, post_samples", [(10, 2045), (1, 4000)])
def test_adc_digital_noise(self, adc_simple_triangle, nshots, post_samples, vref, termination):
""" With the v5 release we noticed some digital noise on the acquired signal.
This test uses a sawtooth signal to check that the noise is between
define boundaries. If it exceed, then we have a problem.
A function generator must be configured to produce a triangle signal
(simpler to process) on all channels. Expected configuration: 2Vpp, 800kHz"""
(simpler to process) on all channels. Expected configuration: 1Vpp, 100kHz"""
for ch in range(4):
conf = PyAdcConf(PyAdcConf.ADC_CONF_TYPE_CHN, ch)
conf.value_set(PyAdcConf.ADC_CONF_CHN_TERMINATION, termination)
......@@ -92,10 +97,10 @@ class TestAdcRegressions(object):
for chan in range(4):
prev_s = None
for i in range(post_samples):
sample = abs(buf.contents.get_sample(chan, i) >> 2)
sample = buf.contents.get_sample(chan, i) >> 2
if prev_s is not None:
# TODO improve future sample computarion (just rule of thumb number now)
assert abs(sample - prev_s) < 500, digital_noise_report(buf, i, chan)
assert abs(sample - prev_s) < 50, digital_noise_report(buf, i, chan, vref)
prev_s = sample
adc_simple_triangle.release_buffer(buf, None)
......@@ -24,19 +24,21 @@ LDFLAGS = -L$(LIBADC)
LDLIBS += -Wl,-Bstatic -ladc
LDLIBS += -Wl,-Bdynamic -lrt
DEMOS := adc-acq
DEMOS += example
PROGS := adc-acq
DEMOS := example
CPPCHECK ?= cppcheck
FLAWFINDER ?= flawfinder
all: demo
all: demo progs
demo: $(DEMOS)
install:
progs: $(PROGS)
install: progs
install -d $(DESTDIR)/bin
install -D $(DEMOS) $(DESTDIR)/bin
install -m 755 $(PROGS) $(DESTDIR)/bin
%: %.c $(LIBADC)/libadc.a
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(LDLIBS)
......@@ -48,6 +50,6 @@ flawfinder:
$(FLAWFINDER) -SQDC --error-level=4 .
clean:
rm -f $(DEMOS) *.o *~
rm -f $(DEMOS) $(PROGS) *.o *~
.PHONY: all, clean
......@@ -84,7 +84,7 @@ static const char *help_msg_opt_desc =
" <n-shots>\n"
" number of consecutive acquisitions\n"
" <offset>*\n"
" offset to apply on input channel\n"
" offset to apply on input channel in micro Volts (the offset range depends on the target board)\n"
" <polarity>\n"
" positive edge/slope: 0, negative edge/slope: 1\n"
" <post-sample>\n"
......@@ -518,18 +518,22 @@ static int fald_acq_channel_configuration(struct adc_dev *adc, char *param)
* 0x00 (0): Open input
*/
switch (range) {
case 100:
case 100000:
range = ADC_CONF_100M14B4CHA_CHN_RANGE_100mV;
bit_scale = 0.05/(1<<15);
break;
case 1:
case 1000000:
range = ADC_CONF_100M14B4CHA_CHN_RANGE_1V;
bit_scale = 0.5/(1<<15);
break;
case 10:
case 10000000:
range = ADC_CONF_100M14B4CHA_CHN_RANGE_10V;
bit_scale = 5.0/(1<<15);
break;
default:
fprintf(stderr, "Invalid range value %u\n", range);
errno = EINVAL;
return -1;
}
adc_set_conf(&cfg, ADC_CONF_CHN_RANGE, range);
case 2:
......@@ -1023,7 +1027,7 @@ static void fald_acq_plot_data(struct adc_buffer *buf, unsigned int ch)
char fname[PATH_MAX];
char cmd[PATH_MAX + 256];
snprintf(fname, sizeof(fname), "/tmp/fmcadc.0x%04x.ch%d.dat", devid, ch);
snprintf(fname, sizeof(fname), "/tmp/fmcadc.0x%04x.ch%u.dat", devid, ch);
if (write_file(fname, ch, data, buf->nsamples) < 0) {
printf("Cannot plot data. Write data into file %s failed.\n", fname);
return;
......@@ -1374,7 +1378,7 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
/* Open the ADC */
adc = adc_open(devname, devid, 0, 0, ADC_F_FLUSH);
adc = adc_open(devname, devid, 0, 0, ADC_F_FLUSH | ADC_F_BUF_VMALLOC);
if (!adc) {
fprintf(stderr, "%s: cannot open device: %s\n",
argv[0], adc_strerror(errno));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment