From f2247d7e6b5fb10f77fe95c536321fa8fa49e89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20Bron=C3=A8s?= <romain.brones@synchrotron-soleil.fr> Date: Tue, 12 Nov 2024 15:05:18 +0100 Subject: [PATCH] Add PSCGEN application --- recipes-app/fofb-pscgen/files/pscgen | 164 +++++++++++++++++++ recipes-app/fofb-pscgen/fofb-pscgen_1.0.bb | 16 ++ recipes-core/images/zup-image-soleil-fofb.bb | 2 +- 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 recipes-app/fofb-pscgen/files/pscgen create mode 100644 recipes-app/fofb-pscgen/fofb-pscgen_1.0.bb diff --git a/recipes-app/fofb-pscgen/files/pscgen b/recipes-app/fofb-pscgen/files/pscgen new file mode 100644 index 0000000..25e620b --- /dev/null +++ b/recipes-app/fofb-pscgen/files/pscgen @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +import deviceaccess +import logging +import argparse +import numpy as np + + +# Prepare logger +logger = logging.getLogger("pscgen") + + +# DAQ device +deviceaccess.setDMapFilePath("/opt/fofb/map/devices.dmap") +appdev = deviceaccess.Device("APPUIO") +appdev.open() + +def configure_raw(idx, rst=None, phincr=None, phoffs=None, scale=None, offset=None): + """ + Configure PSC sinewave generator for a PSCID. + If a parameter is set on 'None', the value is kept unchanged. + + PARAMETERS: + ----------- + idx: int + PSCID of the line to configure + rst: bool + Set the reset bit + phincr: int + Phase increment value. NCO is on 16 bits. + phoffs: int + Phase offset value. NCO is on 16 bits. + scale: int + Amplitude scaler. Unsigned on 16 bits. + offset: int + Amplitude offset. Signed on 16 bits. + """ + _phincr = appdev.getOneDRegisterAccessor(np.uintc, "APP.pscgen_0.TABLE_PHASE_INCR") + _phoffs = appdev.getOneDRegisterAccessor(np.uintc, "APP.pscgen_0.TABLE_PHASE_OFFS") + _scale = appdev.getOneDRegisterAccessor(np.uintc, "APP.pscgen_0.TABLE_SCALE") + _phincr.read() + _phoffs.read() + _scale.read() + + if not rst is None: + if rst==1: + _phoffs[idx] = _phoffs[idx] | 0x10000 + else: + _phoffs[idx] = _phoffs[idx] & 0xFFFF + + if not phincr is None: + _phincr[idx] = phincr + + if not phoffs is None: + _phoffs[idx] = phoffs + (_phoffs[idx] & 0x10000) + + if not scale is None: + _scale[idx] = (_scale[idx] & 0xFFFF0000) + scale + + if not offset is None: + _scale[idx] = (_scale[idx] & 0xFFFF) + (offset<<16) + + _scale.write() + _phincr.write() + _phoffs.write() + +def configure(idx, freq=None, phase=None, amp=None, offset=None): + """ + Convenient method to set PSC sinewave generator with physical inputs (Hz, A, degree) + It calls psc_configure() and return the exact values. + + PARAMETERS: + ----------- + idx: int + PSCID of the line to configure + freq: float + Set the sine wave frequency (Hz, <5000) + A negative value reset the generator. + phase: float + Set the sine wave phase (deg, 0<=phase<360) + amp: float + Peak to peak amplitude of the sine wave (A, <20)) + offset: float + Offset of the sine wave (A, -10<=offset<=10)) + + RETURNS + ------- + idx, rst, phincr, phoffs, scale, offset: + Parameters used to call psc_configure() + """ + + if not freq is None: + if freq > 5000: + raise ValueError("Frequency shall be < 5000") + phincr = int(np.round(freq/10079*2**16)) + rst = 0 + if freq <= 0: + rst = 1 + phincr=None + else: + phincr = None + rst= None + + if not phase is None: + if phase >= 360 or phase < 0: + raise ValueError("Phase shall be 0 <= phase < 360") + phoffs = int(np.round(phase/360*2**16)) + else: + phoffs = None + + if not amp is None: + if amp > 20 or amp < 0: + raise ValueError("Amp shall be in [ 0 ; 20 ] A") + scale = int(0xFFFF * amp/20) + else: + scale=None + + if not offset is None: + if offset > 10 or offset < -10: + raise ValueError("Amp shall be in [ -10 ; 10 ] A") + if offset >= 0: + offset = int(0x7FFF * offset/10) + else: + offset = int(0xFFFF+0x7FFF*offset/10) + else: + offset=None + + logger.debug("rst={} phincr={} phoffs={} scale={} offset={}".format(rst, phincr, phoffs, scale, offset)) + configure_raw(idx, rst, phincr, phoffs, scale, offset) + + return idx, rst, phincr, phoffs, scale, offset + +def reset(): + # Table depth to 100 entries + appdev.write("APP.pscgen_0.TABLE_DEPTH", np.uintc(100)) + + # Ticker rate to 10075 Hz + appdev.write("APP.pscgen_0.TICKER_RATE", np.uintc(100e6//10074)) + + # All line to reset, scale 0, offset 0 + for i in range(100): + configure_raw(i, rst=1, scale=0, offset=0) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="PSC sine wave generator") + + parser.add_argument("--reset", action="store_true", help="Reset configuration. First action performed if selected.") + parser.add_argument("--raw", action="store_true", help="Switch to raw inputs: phase increment, DAC units. All inputs must be given") + + parser.add_argument("pscid", type=int, help="ID of the PSC to configure") + parser.add_argument("-f", type=float, default=None, help="Set frequency in Hz. Use a negative frequency to reset NCO") + parser.add_argument("-p", type=float, default=None, help="Set phase in degree") + parser.add_argument("-a", type=float, default=None, help="Set pk-pk amplitude in A") + parser.add_argument("-o", type=float, default=None, help="Set DC offset in A") + + args = parser.parse_args() + + if args.reset: + reset() + + if args.raw: + configure_raw(args.pscid, 0, int(args.f), int(args.p), int(args.a), int(args.o)) + else: + configure(args.pscid, args.f, args.p, args.a, args.o) diff --git a/recipes-app/fofb-pscgen/fofb-pscgen_1.0.bb b/recipes-app/fofb-pscgen/fofb-pscgen_1.0.bb new file mode 100644 index 0000000..e1e48cf --- /dev/null +++ b/recipes-app/fofb-pscgen/fofb-pscgen_1.0.bb @@ -0,0 +1,16 @@ +SUMMARY = "Simple python tool to use the PSC sinus generator" +SECTION = "opt" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + +SRC_URI = " file://pscgen" + +RDEPENDS_${PN}=" deviceaccess-mapfiles deviceaccess-python-bindings" + +FILES_${PN}+="/usr/bin/pscgen" + +do_install() { + # Add FPGA version reader and FoFb Configurator + install -d ${D}/usr/bin/ + install -m 0755 ${WORKDIR}/pscgen ${D}/usr/bin/pscgen +} diff --git a/recipes-core/images/zup-image-soleil-fofb.bb b/recipes-core/images/zup-image-soleil-fofb.bb index c69ef87..d1c879d 100644 --- a/recipes-core/images/zup-image-soleil-fofb.bb +++ b/recipes-core/images/zup-image-soleil-fofb.bb @@ -28,7 +28,7 @@ IMAGE_INSTALL_append = " deviceaccess" IMAGE_INSTALL_append = " deviceaccess-python-bindings" IMAGE_INSTALL_append = " fofb-opcua-server" IMAGE_INSTALL_append = " fofb-init" -IMAGE_INSTALL_append = " fofb-daqcapt" +IMAGE_INSTALL_append = " fofb-daqcapt fofb-pscgen" IMAGE_INSTALL_append = " nfs-utils" # We do not need that -- GitLab