diff --git a/cmake/daemon.cmake b/cmake/daemon.cmake index 2bc9b225..3b23466a 100644 --- a/cmake/daemon.cmake +++ b/cmake/daemon.cmake @@ -7,7 +7,7 @@ if(NOT WIN32) # added to make program editable in qt-creator on windows find_library(CRYPTOPP crypto++ REQUIRED) find_library(CONFIGPP config++ REQUIRED) if (${MUONDETECTOR_ON_RASPBERRY}) -find_library(PIGPIOD_IF2 pigpiod_if2 REQUIRED) +find_library(LIBGPIOD gpiod REQUIRED) endif () find_library(RT rt REQUIRED) find_library(MOSQUITTO mosquitto REQUIRED) @@ -56,12 +56,13 @@ set(MUONDETECTOR_DAEMON_SOURCE_FILES "${MUONDETECTOR_DAEMON_SRC_DIR}/utility/gpio_mapping.cpp" "${MUONDETECTOR_DAEMON_SRC_DIR}/logengine.cpp" "${MUONDETECTOR_DAEMON_SRC_DIR}/utility/geohash.cpp" + "${MUONDETECTOR_DAEMON_SRC_DIR}/utility/ratebuffer.cpp" "${MUONDETECTOR_DAEMON_SRC_DIR}/utility/kalman_gnss_filter.cpp" "${MUONDETECTOR_DAEMON_SRC_DIR}/geoposmanager.cpp" "${MUONDETECTOR_DAEMON_SRC_DIR}/utility/ratebuffer.cpp" "${MUONDETECTOR_I2C_SOURCE_FILES}" - "${MUONDETECTOR_SPI_SOURCE_FILES}" +# "${MUONDETECTOR_SPI_SOURCE_FILES}" ) @@ -109,12 +110,13 @@ set(MUONDETECTOR_DAEMON_HEADER_FILES "${MUONDETECTOR_DAEMON_HEADER_DIR}/utility/gpio_mapping.h" "${MUONDETECTOR_DAEMON_HEADER_DIR}/logengine.h" "${MUONDETECTOR_DAEMON_HEADER_DIR}/utility/geohash.h" + "${MUONDETECTOR_DAEMON_HEADER_DIR}/utility/ratebuffer.h" "${MUONDETECTOR_DAEMON_HEADER_DIR}/utility/kalman_gnss_filter.h" "${MUONDETECTOR_DAEMON_HEADER_DIR}/geoposmanager.h" "${MUONDETECTOR_DAEMON_HEADER_DIR}/utility/ratebuffer.h" "${MUONDETECTOR_I2C_HEADER_FILES}" - "${MUONDETECTOR_SPI_HEADER_FILES}" + #"${MUONDETECTOR_SPI_HEADER_FILES}" ) set(MUONDETECTOR_LOGIN_SOURCE_FILES @@ -166,7 +168,7 @@ target_include_directories(muondetector-daemon PUBLIC target_link_libraries(muondetector-daemon Qt5::Network Qt5::SerialPort crypto++ - pigpiod_if2 + gpiod rt config++ mosquitto diff --git a/cmake/version.cmake b/cmake/version.cmake index 90377f3d..6c5f231d 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -1,4 +1,4 @@ set(PROJECT_VERSION_MAJOR 2) set(PROJECT_VERSION_MINOR 1) set(PROJECT_VERSION_PATCH 0) -set(PROJECT_VERSION_ADDITIONAL "") +set(PROJECT_VERSION_ADDITIONAL "libgpiod") diff --git a/daemon/config/changelog b/daemon/config/changelog index 80d11e51..847a4db6 100644 --- a/daemon/config/changelog +++ b/daemon/config/changelog @@ -3,7 +3,12 @@ muondetector-daemon (2.1.0) ; urgency=low * Bugfixes: - removed wrong QT connect call which resulted in a warning in log messages after daemon startup - fixed reconnect to MQTT broker after connection was lost temporarily - - fixed bug in I2C subsystem which lead to a stuck bus after certain read operations and preventing detection of a device in the next logical read access + - +6cecacd +May 24, 2023 +Git stats + +1,254 c * Updates: - restructured data logging system: diff --git a/daemon/config/conffiles b/daemon/config/conffiles index 191005ce..b5512dec 100644 --- a/daemon/config/conffiles +++ b/daemon/config/conffiles @@ -1,2 +1 @@ /etc/muondetector/muondetector.conf -/etc/systemd/system/pigpiod.service.d/pigpiod.conf diff --git a/daemon/config/muondetector-daemon.service b/daemon/config/muondetector-daemon.service index 5eab868b..be7291c1 100644 --- a/daemon/config/muondetector-daemon.service +++ b/daemon/config/muondetector-daemon.service @@ -1,8 +1,6 @@ [Unit] Description=muondetector-daemon - Daemon for custom muondetector board -Requires=pigpiod.service -BindsTo=pigpiod.service -After=pigpiod.service sockets.target network-online.target +After=sockets.target network-online.target [Service] Type=simple diff --git a/daemon/config/postinst b/daemon/config/postinst index 61eade5f..af7be275 100755 --- a/daemon/config/postinst +++ b/daemon/config/postinst @@ -1,3 +1,2 @@ #!/bin/bash -e -deb-systemd-helper enable pigpiod.service deb-systemd-helper enable muondetector-daemon.service diff --git a/daemon/config/preinst b/daemon/config/preinst index 99c26d3c..f4e38b93 100755 --- a/daemon/config/preinst +++ b/daemon/config/preinst @@ -1,6 +1,12 @@ #!/bin/bash -e mkdir -p /var/muondetector -useradd muonuser -g users -G dialout,pi,i2c,users -s /usr/sbin/nologin -r -N -M -b /var/muondetector || echo "User already exists" +if id muonuser &> /dev/null; then + # transitional code until all older versions of muondetector-daemon are updated. + echo "muonuser exists, adding to group gpio." + adduser muonuser gpio +else + useradd muonuser -g users -G dialout,pi,i2c,users,gpio -s /usr/sbin/nologin -r -N -M -b /var/muondetector +fi chown muonuser:users /var/muondetector chmod g+w /var/muondetector diff --git a/daemon/include/daemon.h b/daemon/include/daemon.h index 79e88a5f..7a4d4277 100644 --- a/daemon/include/daemon.h +++ b/daemon/include/daemon.h @@ -21,6 +21,7 @@ #include #include "logengine.h" +#include "utility/ratebuffer.h" #include "logparameter.h" #include "pigpiodhandler.h" #include "hardware/spidevices.h" @@ -121,7 +122,8 @@ public slots: void onGpsMonHW2Updated(const GnssMonHw2Struct& hw2); void receivedTcpMessage(TcpMessage tcpMessage); void pollAllUbxMsgRate(); - void sendGpioPinEvent(uint8_t gpio_pin); + void onGpioPinEvent(unsigned int gpio, EventTime event_time); + void sendGpioPinEvent(unsigned int gpio, EventTime event_time); void onGpsPropertyUpdatedGeodeticPos(const GnssPosStruct& pos); void UBXReceivedVersion(const QString& swString, const QString& hwString, const QString& protString); void sampleAdc0Event(); @@ -153,11 +155,11 @@ public slots: void UBXSetMinMaxSVs(uint8_t minSVs, uint8_t maxSVs); void UBXSetMinCNO(uint8_t minCNO); void GpioSetInput(unsigned int gpio); - void GpioSetOutput(unsigned int gpio); + void GpioSetOutput(unsigned int gpio, bool initState = false); void GpioSetPullUp(unsigned int gpio); void GpioSetPullDown(unsigned int gpio); void GpioSetState(unsigned int gpio, bool state); - void GpioRegisterForCallback(unsigned int gpio, bool edge); // false=falling, true=rising + void GpioRegisterForCallback(unsigned int gpio, PigpiodHandler::EventEdge edge); // false=falling, true=rising void UBXSetCfgTP5(const UbxTimePulseStruct& tp); void UBXSetAopCfg(bool enable = true, uint16_t maxOrbErr = 0); void UBXSaveCfg(uint8_t devMask = QtSerialUblox::DEV_BBR | QtSerialUblox::DEV_FLASH); @@ -165,6 +167,7 @@ public slots: void timeMarkIntervalCountUpdate(uint16_t newCounts, double lastInterval); void requestMqttConnectionStatus(); void eventMessage(const QString& messageString); + void eventInterval(quint64 nsecs); private slots: void onRateBufferReminder(); @@ -227,7 +230,15 @@ private slots: std::shared_ptr> io_extender_p; std::shared_ptr oled_p {}; std::shared_ptr ublox_i2c_p {}; - + /* + float biasVoltage = 0.; + bool biasON = false; + GPIO_SIGNAL eventTrigger { UNDEFINED_SIGNAL }; + bool gainSwitch = false; + bool preampStatus[2]; + uint8_t pcaPortMask = 0; + QVector dacThresh; // do not give values here because of push_back in constructor of deamon +*/ QPointer pigHandler; QPointer tdc7200; bool spiDevicePresent = false; diff --git a/daemon/include/pigpiodhandler.h b/daemon/include/pigpiodhandler.h index 90bf94f1..ced13b9e 100644 --- a/daemon/include/pigpiodhandler.h +++ b/daemon/include/pigpiodhandler.h @@ -10,23 +10,38 @@ #include "utility/gpio_mapping.h" #include +#include +#include +#include +#include -#define XOR_RATE 0 -#define AND_RATE 1 -#define COMBINED_RATE 2 - -static QVector DEFAULT_VECTOR; +#include class PigpiodHandler : public QObject { Q_OBJECT public: - explicit PigpiodHandler(QVector gpioPins = DEFAULT_VECTOR, unsigned int spi_freq = 61035, - uint32_t spi_flags = 0, QObject* parent = nullptr); - // can't make it private because of access of PigpiodHandler with global pointer + enum PinBias : std::uint8_t { + BiasDisabled = 0x00, + PullDown = 0x01, + PullUp = 0x02, + ActiveLow = 0x04, + OpenDrain = 0x08, + OpenSource = 0x10 + }; + enum class EventEdge { + RisingEdge, + FallingEdge, + BothEdges + }; + + static constexpr unsigned int UNDEFINED_GPIO { 256 }; + + explicit PigpiodHandler(std::vector gpioPins, QObject* parent = nullptr); + ~PigpiodHandler(); + QDateTime startOfProgram, lastSamplingTime; // the exact time when the program starts (Utc) QElapsedTimer elapsedEventTimer; - GPIO_SIGNAL samplingTriggerSignal = EVT_XOR; double clockMeasurementSlope = 0.; double clockMeasurementOffset = 0.; @@ -34,54 +49,41 @@ class PigpiodHandler : public QObject { quint64 lastTimeMeasurementTick = 0; bool isInhibited() const { return inhibit; } - void setInhibited(bool inh = true) { inhibit = inh; } signals: - void signal(uint8_t gpio_pin); - void samplingTrigger(); - void eventInterval(quint64 nsecs); - void timePulseDiff(qint32 usecs); - - // spi related signals - void spiData(uint8_t reg, std::string data); + void event(unsigned int gpio_pin, EventTime timestamp); public slots: + void start(); void stop(); bool initialised(); - void setInput(unsigned int gpio); - void setOutput(unsigned int gpio); - void setPullUp(unsigned int gpio); - void setPullDown(unsigned int gpio); - void setGpioState(unsigned int gpio, bool state); - void setSamplingTriggerSignal(GPIO_SIGNAL signalName) { samplingTriggerSignal = signalName; } - void registerForCallback(unsigned int gpio, bool edge); // false=falling, true=rising - - // spi related slots - void writeSpi(uint8_t command, std::string data); - void readSpi(uint8_t command, unsigned int bytesToRead); + bool setPinInput(unsigned int gpio); + bool setPinOutput(unsigned int gpio, bool initState); + + bool setPinBias(unsigned int gpio, std::uint8_t bias_flags); + bool setPinState(unsigned int gpio, bool state); + bool registerInterrupt(unsigned int gpio, EventEdge edge); + bool unRegisterInterrupt(unsigned int gpio); + void setInhibited(bool inh = true) { inhibit = inh; } private: - bool isInitialised = false; - bool spiInitialised = false; - bool spiInitialise(); // will be executed at first spi read/write command - bool isSpiInitialised(); - unsigned int spiClkFreq; //3906250; // TDC7200: up to 20MHz SCLK - // Raspi: Core clock speed of 250MHz can be devided by any even number from 2 to 65536 - // => 3.814kHz to 125MHz - /* - * spi_flags consists of the least significant 22 bits. - - 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - b b b b b b R T n n n n W A u2 u1 u0 p2 p1 p0 m m - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - word size bits msb msb only-3wire 3wire aux CEx? activ-low? spi-mode - */ - unsigned int spiFlags; // fixed value for now + bool isInitialised { false }; + QTimer gpioClockTimeMeasurementTimer; void measureGpioClockTime(); - bool inhibit = false; - int verbose = 0; + void reloadInterruptSettings(); + [[gnu::hot]] void eventHandler(struct gpiod_line* line); + [[gnu::hot]] void processEvent(unsigned int gpio, std::shared_ptr line_event); + + bool inhibit { false }; + int verbose { 0 }; + gpiod_chip* fChip { nullptr }; + std::map fInterruptLineMap {}; + std::map fLineMap {}; + bool fThreadRunning { false }; + std::map> fThreads {}; + std::mutex fMutex; }; #endif // PIGPIODHANDLER_H diff --git a/daemon/include/utility/gpio_mapping.h b/daemon/include/utility/gpio_mapping.h index 54b0ed65..2f3e25bc 100644 --- a/daemon/include/utility/gpio_mapping.h +++ b/daemon/include/utility/gpio_mapping.h @@ -5,11 +5,9 @@ #define MAX_HW_VER 3 -// define pins on the raspberry pi, UBIAS_EN is the power on/off pin for bias voltage -// PREAMP_1/2 enables the DC voltage to power the preamp through the signal cable +// Mapping between signals of the MuonPi hardware interface and the actual GPIO pins of the RaspberryPi // ATTENTION: -// TO MAKE IT MORE SIMPLE THERE WILL BE ONLY PIGPIO NAMING STANDARD, -// NO WIRING PI FROM NOW +// All pins are numbered according to the BCM designation static const std::map GPIO_PINMAP_VERSIONS[MAX_HW_VER + 1] = { { @@ -27,9 +25,9 @@ static const std::map GPIO_PINMAP_VERSIONS[MAX_HW_VER { STATUS1, 13 }, { STATUS2, 19 }, { STATUS3, 26 }, - { TDC_INTB, 20 }, - { TDC_STATUS, 21 }, - { EXT_TRIGGER, 21 } }, + { TDC_INTB, 24 }, + { TDC_STATUS, 25 }, + { EXT_TRIGGER, 16 } }, { /* Pin mapping, HW Version 2 */ { UBIAS_EN, 26 }, { PREAMP_1, 4 }, @@ -74,7 +72,7 @@ inline GPIO_SIGNAL bcmToGpioSignal(unsigned int bcmGpioNumber) while (i != GPIO_PINMAP.cend() && i->second != bcmGpioNumber) ++i; if (i == GPIO_PINMAP.cend()) - return UNDEFINED_PIN; + return UNDEFINED_SIGNAL; return i->first; } diff --git a/daemon/include/utility/ratebuffer.h b/daemon/include/utility/ratebuffer.h index 2daceabe..426c4451 100644 --- a/daemon/include/utility/ratebuffer.h +++ b/daemon/include/utility/ratebuffer.h @@ -10,9 +10,9 @@ using namespace std::literals; -constexpr double MAX_AVG_RATE { 100. }; -constexpr unsigned long MAX_BURST_MULTIPLICITY { 10 }; -constexpr std::chrono::microseconds MAX_BUFFER_TIME { 60s }; +constexpr double MAX_AVG_RATE { 500. }; +constexpr unsigned long MAX_BURST_MULTIPLICITY { 20 }; +constexpr std::chrono::microseconds MAX_BUFFER_TIME { 30s }; constexpr std::chrono::microseconds MAX_DEADTIME { static_cast(1e+6 / MAX_AVG_RATE) }; constexpr std::chrono::microseconds DEADTIME_INCREMENT { 50 }; @@ -65,12 +65,12 @@ class EventRateBuffer : public QObject { [[nodiscard]] auto lastEventTime() const -> EventTime; signals: - void filteredEvent(uint8_t gpio, EventTime event_time); - void eventIntervalSignal(uint8_t gpio, std::chrono::nanoseconds ns); + void filteredEvent(unsigned int gpio, EventTime event_time); + void eventIntervalSignal(unsigned int gpio, std::chrono::nanoseconds ns); public slots: - //void onEvent(unsigned int gpio, EventTime event_time); - void onEvent(uint8_t gpio); + void onEvent(unsigned int gpio, EventTime event_time); + //void onEvent(uint8_t gpio); private: double fRateLimit { MAX_AVG_RATE }; diff --git a/daemon/src/daemon.cpp b/daemon/src/daemon.cpp index 624170f8..27648110 100644 --- a/daemon/src/daemon.cpp +++ b/daemon/src/daemon.cpp @@ -28,6 +28,9 @@ #include #define DEGREE_CHARCODE 248 +#define XOR_RATE 0 +#define AND_RATE 1 +#define COMBINED_RATE 2 using namespace std; using namespace MuonPi; @@ -589,6 +592,9 @@ Daemon::Daemon(configuration cfg, QObject* parent) // create network discovery service networkDiscovery = new NetworkDiscovery(NetworkDiscovery::DeviceType::DAEMON, daemonPort, this); + // set up histograms + setupHistos(); + // connect to the pigpio daemon interface for gpio control connectToPigpiod(); @@ -596,12 +602,32 @@ Daemon::Daemon(configuration cfg, QObject* parent) for (auto [signal, pin] : GPIO_PINMAP) { if (GPIO_SIGNAL_MAP.at(signal).direction == DIR_IN) { auto ratebuf = std::make_shared(pin); - connect(pigHandler, &PigpiodHandler::signal, ratebuf.get(), &EventRateBuffer::onEvent); - // connect(&ratebuf, &EventRateBuffer::filteredEvent, this, &Daemon::sendGpioPinEvent); + connect(pigHandler, &PigpiodHandler::event, ratebuf.get(), &EventRateBuffer::onEvent); + connect(ratebuf.get(), &EventRateBuffer::filteredEvent, this, &Daemon::sendGpioPinEvent); + connect(ratebuf.get(), &EventRateBuffer::filteredEvent, this, &Daemon::onGpioPinEvent); + + connect(ratebuf.get(), &EventRateBuffer::eventIntervalSignal, this, [this](unsigned int gpio, std::chrono::nanoseconds ns) { + if (m_histo_map.find("gpioEventInterval") != m_histo_map.end() + && (GPIO_PINMAP[config.eventTrigger] == gpio)) { + m_histo_map["gpioEventInterval"]->fill(1e-6 * ns.count()); + } + }); + m_gpio_ratebuffers.emplace(pin, ratebuf); } } + connect(this, &Daemon::eventInterval, this, [this](quint64 nsecs) + // connect(&rateBuffer, &RateBuffer::eventIntervalSignal, this, [this](unsigned int gpio, std::chrono::nanoseconds ns) + { + if (m_histo_map.find("gpioEventIntervalShort") != m_histo_map.end()) { + const long usecs = nsecs / 1000L; + if (usecs <= m_histo_map["gpioEventIntervalShort"]->getMax()) { + m_histo_map["gpioEventIntervalShort"]->fill(usecs); + } + } + }); + // set up histograms setupHistos(); @@ -708,73 +734,29 @@ Daemon::~Daemon() void Daemon::connectToPigpiod() { - const QVector gpio_pins({ GPIO_PINMAP[EVT_AND], GPIO_PINMAP[EVT_XOR], - GPIO_PINMAP[TIMEPULSE], GPIO_PINMAP[EXT_TRIGGER] }); + /* + const QVector gpio_pins({ GPIO_PINMAP[EVT_AND], GPIO_PINMAP[EVT_XOR], + GPIO_PINMAP[TIMEPULSE], GPIO_PINMAP[EXT_TRIGGER] }); +*/ + const std::vector gpio_pins({ GPIO_PINMAP[EVT_AND], GPIO_PINMAP[EVT_XOR], + GPIO_PINMAP[TIMEPULSE] }); pigHandler = new PigpiodHandler(gpio_pins); - tdc7200 = new TDC7200(GPIO_PINMAP[TDC_INTB]); pigThread = new QThread(); pigThread->setObjectName("muondetector-daemon-pigpio"); pigHandler->moveToThread(pigThread); - tdc7200->moveToThread(pigThread); connect(this, &Daemon::aboutToQuit, pigThread, &QThread::quit); connect(pigThread, &QThread::finished, pigThread, &QThread::deleteLater); - //pighandler <-> tdc - connect(pigHandler, &PigpiodHandler::spiData, tdc7200, &TDC7200::onDataReceived); - connect(pigHandler, &PigpiodHandler::signal, tdc7200, &TDC7200::onDataAvailable); - connect(tdc7200, &TDC7200::readData, pigHandler, &PigpiodHandler::readSpi); - connect(tdc7200, &TDC7200::writeData, pigHandler, &PigpiodHandler::writeSpi); - - //tdc <-> thread & daemon - connect(tdc7200, &TDC7200::tdcEvent, this, [this](double usecs) { - if (m_histo_map.find("Time-to-Digital Time Diff") != m_histo_map.end()) { - m_histo_map["Time-to-Digital Time Diff"]->fill(usecs); - } - }); - connect(tdc7200, &TDC7200::statusUpdated, this, [this](bool isPresent) { - spiDevicePresent = isPresent; - sendSpiStats(); - }); - connect(pigThread, &QThread::started, tdc7200, &TDC7200::initialise); - connect(pigThread, &QThread::finished, tdc7200, &TDC7200::deleteLater); - //pigHandler <-> thread & daemon connect(this, &Daemon::aboutToQuit, pigHandler, &PigpiodHandler::stop); connect(pigThread, &QThread::finished, pigHandler, &PigpiodHandler::deleteLater); - connect(this, &Daemon::GpioSetOutput, pigHandler, &PigpiodHandler::setOutput); - connect(this, &Daemon::GpioSetInput, pigHandler, &PigpiodHandler::setInput); - connect(this, &Daemon::GpioSetPullUp, pigHandler, &PigpiodHandler::setPullUp); - connect(this, &Daemon::GpioSetPullDown, pigHandler, &PigpiodHandler::setPullDown); - connect(this, &Daemon::GpioSetState, pigHandler, &PigpiodHandler::setGpioState); - connect(this, &Daemon::GpioRegisterForCallback, pigHandler, &PigpiodHandler::registerForCallback); - connect(pigHandler, &PigpiodHandler::signal, this, &Daemon::sendGpioPinEvent); - - connect(pigHandler, &PigpiodHandler::signal, this, [this](uint8_t gpio_pin) { - if (gpio_pin == GPIO_PINMAP[TIME_MEAS_OUT]) { - onStatusLed2Event(50); - } - }); - - connect(pigHandler, &PigpiodHandler::samplingTrigger, this, &Daemon::sampleAdc0Event); - connect(pigHandler, &PigpiodHandler::eventInterval, this, [this](quint64 nsecs) { - if (m_histo_map.find("gpioEventInterval") != m_histo_map.end()) { - m_histo_map["gpioEventInterval"]->fill(1e-6 * nsecs); - } - }); - connect(pigHandler, &PigpiodHandler::eventInterval, this, [this](quint64 nsecs) { - if (m_histo_map.find("gpioEventIntervalShort") != m_histo_map.end()) { - if (nsecs / 1000 <= m_histo_map["gpioEventIntervalShort"]->getMax()) - m_histo_map["gpioEventIntervalShort"]->fill((double)nsecs / 1000.); - } - }); - connect(pigHandler, &PigpiodHandler::timePulseDiff, this, [this](qint32 usecs) { - if (m_histo_map.find("TPTimeDiff") != m_histo_map.end()) { - m_histo_map["TPTimeDiff"]->fill((double)usecs); - } - }); - pigHandler->setSamplingTriggerSignal(config.eventTrigger); - connect(this, &Daemon::setSamplingTriggerSignal, pigHandler, &PigpiodHandler::setSamplingTriggerSignal); - + connect(this, &Daemon::GpioSetOutput, pigHandler, &PigpiodHandler::setPinOutput); + connect(this, &Daemon::GpioSetInput, pigHandler, &PigpiodHandler::setPinInput); + // connect(this, &Daemon::GpioSetPullUp, pigHandler, &PigpiodHandler::setPullUp); + // connect(this, &Daemon::GpioSetPullDown, pigHandler, &PigpiodHandler::setPullDown); + connect(this, &Daemon::GpioSetState, pigHandler, &PigpiodHandler::setPinState); + connect(this, &Daemon::GpioRegisterForCallback, pigHandler, &PigpiodHandler::registerInterrupt); + //connect(pigHandler, &PigpiodHandler::signal, this, &Daemon::sendGpioPinEvent); struct timespec ts_res; clock_getres(CLOCK_REALTIME, &ts_res); if (verbose > 3) { @@ -783,14 +765,7 @@ void Daemon::connectToPigpiod() timespec_get(&lastRateInterval, TIME_UTC); startOfProgram = lastRateInterval; - connect(pigHandler, &PigpiodHandler::signal, this, [this](uint8_t gpio_pin) { - rateCounterIntervalActualisation(); - if (gpio_pin == GPIO_PINMAP[EVT_XOR]) { - xorCounts.back()++; - } else if (gpio_pin == GPIO_PINMAP[EVT_AND]) { - andCounts.back()++; - } - }); + pigThread->start(); rateBufferReminder.setInterval(rateBufferInterval); rateBufferReminder.setSingleShot(false); @@ -809,16 +784,22 @@ void Daemon::connectToPigpiod() emit GpioSetState(GPIO_PINMAP[STATUS1], 0); emit GpioSetState(GPIO_PINMAP[STATUS2], 0); emit GpioSetPullUp(GPIO_PINMAP[ADC_READY]); - emit GpioRegisterForCallback(GPIO_PINMAP[ADC_READY], 1); + //emit GpioRegisterForCallback(GPIO_PINMAP[ADC_READY], PigpiodHandler::EventEdge::RisingEdge); + auto it = GPIO_PINMAP.find(config.eventTrigger); + if (it != GPIO_PINMAP.end()) { + // pigHandler->setSamplingTriggerSignal( GPIO_PINMAP[eventTrigger] ); + emit GpioRegisterForCallback(GPIO_PINMAP[config.eventTrigger], PigpiodHandler::EventEdge::RisingEdge); + } + // connect(this, &Daemon::setSamplingTriggerSignal, pigHandler, &PigpiodHandler::setSamplingTriggerSignal); if (MuonPi::Version::hardware.major > 1) { - emit GpioSetInput(GPIO_PINMAP[PREAMP_FAULT]); - emit GpioRegisterForCallback(GPIO_PINMAP[PREAMP_FAULT], 0); + //emit GpioSetInput(GPIO_PINMAP[PREAMP_FAULT]); + emit GpioRegisterForCallback(GPIO_PINMAP[PREAMP_FAULT], PigpiodHandler::EventEdge::FallingEdge); emit GpioSetPullUp(GPIO_PINMAP[PREAMP_FAULT]); - emit GpioSetInput(GPIO_PINMAP[TDC_INTB]); - emit GpioRegisterForCallback(GPIO_PINMAP[TDC_INTB], 0); - emit GpioSetInput(GPIO_PINMAP[TIME_MEAS_OUT]); - emit GpioRegisterForCallback(GPIO_PINMAP[TIME_MEAS_OUT], 0); + //emit GpioSetInput(GPIO_PINMAP[TDC_INTB]); + emit GpioRegisterForCallback(GPIO_PINMAP[TDC_INTB], PigpiodHandler::EventEdge::FallingEdge); + //emit GpioSetInput(GPIO_PINMAP[TIME_MEAS_OUT]); + emit GpioRegisterForCallback(GPIO_PINMAP[TIME_MEAS_OUT], PigpiodHandler::EventEdge::RisingEdge); } if (MuonPi::Version::hardware.major >= 3) { emit GpioSetOutput(GPIO_PINMAP[IN_POL1]); @@ -1382,12 +1363,68 @@ void Daemon::sendDacReadbackValue(uint8_t channel, float voltage) emit sendTcpMessage(tcpMessage); } -void Daemon::sendGpioPinEvent(uint8_t gpio_pin) +void Daemon::onGpioPinEvent(unsigned int gpio, EventTime event_time) +{ + // reverse lookup of gpio function from given pin (first occurence) + auto result = std::find_if(GPIO_PINMAP.begin(), GPIO_PINMAP.end(), [&gpio](const std::pair& item) { return item.second == gpio; }); + if (result != GPIO_PINMAP.end()) { + if (result->first == TIMEPULSE) { + auto usecs = std::chrono::duration_cast(event_time.time_since_epoch()).count(); + usecs = usecs % 1000000LL; + if (usecs > 500000L) { + usecs -= 1000000LL; + } + if (m_histo_map.find("TPTimeDiff") != m_histo_map.end()) { + m_histo_map["TPTimeDiff"]->fill((double)usecs); + } + if (verbose > 2) + qDebug() << "TP time diff:" << usecs << "us"; + } else if (result->first == EVT_XOR || result->first == EVT_AND) { + rateCounterIntervalActualisation(); + if (result->first == EVT_XOR) { + xorCounts.back()++; + } else if (result->first == EVT_AND) { + andCounts.back()++; + } + } + if (result->first == config.eventTrigger) { + + auto nsecs = m_gpio_ratebuffers.at(gpio)->lastInterval().count(); + if ( nsecs > 0 ) { + emit eventInterval( nsecs ); + } + + emit sampleAdc0Event(); + } + + if (result->first == TIME_MEAS_OUT) { + auto start_gpio = GPIO_PINMAP.find(config.eventTrigger); + if (start_gpio != GPIO_PINMAP.end()) { + long long nsecs { 0 }; + if (result->first != config.eventTrigger) { + nsecs = (m_gpio_ratebuffers.at(gpio)->lastEventTime() - m_gpio_ratebuffers.at(start_gpio->second)->lastEventTime()).count(); + //nsecs = rateBuffer.lastInterval(gpio, start_gpio->second).count(); + //if (nsecs < 0) + // nsecs = rateBuffer.lastInterval(start_gpio->second, gpio).count(); + } else { + nsecs = m_gpio_ratebuffers.at(gpio)->lastInterval().count(); + //nsecs = rateBuffer.lastInterval(gpio).count(); + } + if (std::abs(nsecs) < 100'000L) { + emit eventInterval(nsecs); + } + } + onStatusLed2Event(10); + } + } +} + +void Daemon::sendGpioPinEvent(unsigned int gpio, EventTime event_time) { - TcpMessage tcpMessage(TCP_MSG_KEY::MSG_GPIO_EVENT); // reverse lookup of gpio function from given pin (first occurence) - auto result = std::find_if(GPIO_PINMAP.begin(), GPIO_PINMAP.end(), [&gpio_pin](const std::pair& item) { return item.second == gpio_pin; }); + auto result = std::find_if(GPIO_PINMAP.begin(), GPIO_PINMAP.end(), [&gpio](const std::pair& item) { return item.second == gpio; }); if (result != GPIO_PINMAP.end()) { + TcpMessage tcpMessage(TCP_MSG_KEY::MSG_GPIO_EVENT); *(tcpMessage.dStream) << (GPIO_SIGNAL)result->first; emit sendTcpMessage(tcpMessage); } @@ -1450,7 +1487,7 @@ void Daemon::sendEventTriggerSelection() if (pigHandler == nullptr) return; TcpMessage tcpMessage(TCP_MSG_KEY::MSG_EVENTTRIGGER); - *(tcpMessage.dStream) << (GPIO_SIGNAL)pigHandler->samplingTriggerSignal; + *(tcpMessage.dStream) << config.eventTrigger; emit sendTcpMessage(tcpMessage); } @@ -1672,10 +1709,11 @@ void Daemon::setEventTriggerSelection(GPIO_SIGNAL signal) return; if (verbose > 0) { - qInfo() << "changed event selection to signal" << (unsigned int)signal; + qInfo() << "changed event selection to signal" << QString::fromStdString(GPIO_SIGNAL_MAP.at(signal).name); } - emit setSamplingTriggerSignal(signal); - emit logParameter(LogParameter("gpioTriggerSelection", "0x" + QString::number((int)pigHandler->samplingTriggerSignal, 16), LogParameter::LOG_EVERY)); + emit GpioRegisterForCallback(it->second, PigpiodHandler::EventEdge::RisingEdge); + config.eventTrigger = signal; + emit logParameter(LogParameter("gpioTriggerSelection", "0x" + QString::number((int)signal, 16), LogParameter::LOG_EVERY)); } // ALL FUNCTIONS ABOUT SETTINGS FOR THE I2C-DEVICES (DAC, ADC, PCA...) @@ -2377,7 +2415,7 @@ void Daemon::onLogParameterPolled() if (io_extender_p && io_extender_p->probeDevicePresence()) emit logParameter(LogParameter("ubxInputSwitch", "0x" + QString::number(config.pcaPortMask, 16), LogParameter::LOG_ON_CHANGE)); if (pigHandler != nullptr) - emit logParameter(LogParameter("gpioTriggerSelection", "0x" + QString::number((int)pigHandler->samplingTriggerSignal, 16), LogParameter::LOG_ON_CHANGE)); + emit logParameter(LogParameter("gpioTriggerSelection", "0x" + QString::number((int)config.eventTrigger, 16), LogParameter::LOG_ON_CHANGE)); for (auto& [name, hist] : m_histo_map) { sendHistogram(*hist); diff --git a/daemon/src/main.cpp b/daemon/src/main.cpp index 2a7cd1fe..6d6ed458 100644 --- a/daemon/src/main.cpp +++ b/daemon/src/main.cpp @@ -103,7 +103,9 @@ int main(int argc, char* argv[]) qRegisterMetaType("ADC_SAMPLING_MODE"); qRegisterMetaType("MuonPi::Version::Version"); qRegisterMetaType("UbxDynamicModel"); - + qRegisterMetaType("EventTime"); + qRegisterMetaType("PigpiodHandler::EventEdge"); + qInstallMessageHandler(messageOutput); QCoreApplication a(argc, argv); diff --git a/daemon/src/pigpiodhandler.cpp b/daemon/src/pigpiodhandler.cpp index 6f680a2a..3b1c56fa 100644 --- a/daemon/src/pigpiodhandler.cpp +++ b/daemon/src/pigpiodhandler.cpp @@ -1,3 +1,4 @@ +#include "pigpiodhandler.h" #include "utility/gpio_mapping.h" #include #include @@ -5,21 +6,24 @@ #include #include #include -#include +#include #include #include -extern "C" { -#include -} - -static int pi = -1; -static int spiHandle = -1; -static QPointer pigHandlerAddress; // QPointer automatically clears itself if pigHandler object is destroyed +constexpr char CONSUMER[] = "muonpi"; +const std::string chipname { "/dev/gpiochip0" }; template inline static T sqr(T x) { return x * x; } +constexpr std::chrono::nanoseconds timespecToDuration(timespec ts) +{ + auto duration = std::chrono::seconds{ts.tv_sec} + + std::chrono::nanoseconds{ts.tv_nsec}; + + return std::chrono::duration_cast(duration); +} + /* linear regression algorithm taken from: @@ -75,292 +79,345 @@ void calcLinearCoefficients(int n, quint64* xarray, qint64* yarray, *offs = (double)(b + offsy); } -/* This is the central interrupt routine for all registered GPIO pins - * - */ -static void cbFunction(int user_pi, unsigned int user_gpio, - unsigned int level, uint32_t tick) -{ - if (pigHandlerAddress.isNull()) { - pigpio_stop(pi); - return; - } - if (pi != user_pi) { - // put some error here for the case pi is not the same as before initialized - return; - } - - QPointer pigpioHandler = pigHandlerAddress; - - if (pigpioHandler->isInhibited()) - return; - - static uint32_t lastTriggerTick = 0; - static uint32_t lastTick = 0; - static uint16_t pileupCounter = 0; - - // look, if the last event occured just recently - // if so, count the pileup counter up - // count down if not - if (tick - lastTick < MuonPi::Config::event_count_deadtime_ticks) { - pileupCounter++; - // if more than a certain number of pileups happened in a short period of time, leave immediately - if (pileupCounter > MuonPi::Config::event_count_max_pileups) { - pileupCounter = MuonPi::Config::event_count_max_pileups; - lastTick = tick; - return; - } - } else if (pileupCounter > 0) { - pileupCounter--; - } - - lastTick = tick; - - try { - // allow only registered signals to be processed here - // if gpio pin fired which is not in GPIO_PIN list, return immediately - auto it = std::find_if(GPIO_PINMAP.cbegin(), GPIO_PINMAP.cend(), - [&user_gpio](const std::pair& val) { - if (val.second == user_gpio) - return true; - return false; - }); - if (it == GPIO_PINMAP.end()) - return; - - QDateTime now = QDateTime::currentDateTimeUtc(); - - if (user_gpio == GPIO_PINMAP[pigpioHandler->samplingTriggerSignal]) { - if (pigpioHandler->lastSamplingTime.msecsTo(now) >= MuonPi::Config::Hardware::ADC::deadtime.count()) { - emit pigpioHandler->samplingTrigger(); - pigpioHandler->lastSamplingTime = now; - } - /* quint64 nsecsElapsed = pigpioHandler->elapsedEventTimer.nsecsElapsed(); */ - pigpioHandler->elapsedEventTimer.start(); - emit pigpioHandler->eventInterval((tick - lastTriggerTick) * 1000); - lastTriggerTick = tick; - } - - if (user_gpio == GPIO_PINMAP[TIMEPULSE]) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - quint64 timestamp = pigpioHandler->gpioTickOverflowCounter + tick; - quint64 t0 = pigpioHandler->startOfProgram.toMSecsSinceEpoch(); - - long double meanDiff = pigpioHandler->clockMeasurementOffset; - - long double dx = timestamp - pigpioHandler->lastTimeMeasurementTick; - long double dy = pigpioHandler->clockMeasurementSlope * dx; - meanDiff += dy; - - qint64 meanDiffInt = (qint64)meanDiff; - double meanDiffFrac = (meanDiff - (qint64)meanDiff); - timestamp += meanDiffInt; // add diff to real time - long int ts_sec = timestamp / 1000000 + (t0 / 1000); // conv. us to s - long int ts_nsec = 1000 * (timestamp % 1000000) + (t0 % 1000) * 1000000L; - ts_nsec += (long int)(1000. * meanDiffFrac); - - long double ppsOffs = (ts_sec - ts.tv_sec) + ts_nsec * 1e-9; - if (std::fabs(ppsOffs) < 3600.) { - qint32 t_diff_us = (double)(ppsOffs)*1e6; - emit pigpioHandler->timePulseDiff(t_diff_us); - } - } - - emit pigpioHandler->signal(user_gpio); - - // level gives the information if it is up or down (only important if trigger is - // at both: rising and falling edge) - } catch (std::exception& e) { - pigpioHandler = 0; - pigpio_stop(pi); - qCritical() << "Exception catched in 'static void cbFunction(int user_pi, unsigned int user_gpio, unsigned int level, uint32_t tick)':" << e.what(); - qCritical() << "with user_pi=" << user_pi << "user_gpio=" << user_gpio << "level=" << level << "tick=" << tick; - } -} - -PigpiodHandler::PigpiodHandler(QVector gpioPins, unsigned int spi_freq, uint32_t spi_flags, QObject* parent) +PigpiodHandler::PigpiodHandler(std::vector gpioPins, QObject* parent) : QObject(parent) { startOfProgram = QDateTime::currentDateTimeUtc(); lastSamplingTime = startOfProgram; elapsedEventTimer.start(); - pigHandlerAddress = this; - spiClkFreq = spi_freq; - spiFlags = spi_flags; - pi = pigpio_start((char*)"127.0.0.1", (char*)"8888"); - if (pi < 0) { - qFatal("Could not connect to pigpio daemon. Is pigpiod running? Start with sudo pigpiod -s 1"); + + fChip = gpiod_chip_open(chipname.c_str()); + if (fChip == nullptr) { + qCritical() << "error opening gpio chip " << QString::fromStdString(chipname); + throw std::exception(); return; } - isInitialised = true; - for (auto& gpioPin : gpioPins) { - set_mode(pi, gpioPin, PI_INPUT); - - int result = callback(pi, gpioPin, RISING_EDGE, cbFunction); - if (result < 0) { - qCritical() << "error registering gpio callback for BCM pin" << gpioPin; + for (unsigned int gpioPin : gpioPins) { + gpiod_line* line = gpiod_chip_get_line(fChip, gpioPin); + int flags = 0; + /* + GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN | + GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE | + GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + */ + int ret = gpiod_line_request_rising_edge_events_flags(line, CONSUMER, flags); + if (ret < 0) { + qCritical() << "Request for registering gpio line" << gpioPin << "for event notification failed"; + continue; } + fInterruptLineMap.emplace(std::make_pair(gpioPin, line)); } - gpioClockTimeMeasurementTimer.setInterval(MuonPi::Config::Hardware::GPIO::Clock::Measurement::interval); + + gpioClockTimeMeasurementTimer.setInterval(MuonPi::Config::Hardware::GPIO::Clock::Measurement::interval /*GPIO_CLOCK_MEASUREMENT_INTERVAL_MS*/); gpioClockTimeMeasurementTimer.setSingleShot(false); connect(&gpioClockTimeMeasurementTimer, &QTimer::timeout, this, &PigpiodHandler::measureGpioClockTime); gpioClockTimeMeasurementTimer.start(); -} -void PigpiodHandler::setInput(unsigned int gpio) -{ - if (isInitialised) - set_mode(pi, gpio, PI_INPUT); + reloadInterruptSettings(); } -void PigpiodHandler::setOutput(unsigned int gpio) +PigpiodHandler::~PigpiodHandler() { - if (isInitialised) - set_mode(pi, gpio, PI_OUTPUT); + this->stop(); + for (auto [gpio, line] : fInterruptLineMap) { + gpiod_line_release(line); + } + for (auto [gpio, line] : fLineMap) { + gpiod_line_release(line); + } + if (fChip != nullptr) + gpiod_chip_close(fChip); } -void PigpiodHandler::setPullUp(unsigned int gpio) +void PigpiodHandler::processEvent(unsigned int gpio, std::shared_ptr line_event) { - if (isInitialised) - set_pull_up_down(pi, gpio, PI_PUD_UP); + EventTime timestamp{timespecToDuration(line_event->ts)}; + emit event(gpio, timestamp); + if (verbose > 3) { + qDebug() << "line event: gpio" << gpio << " edge: " + << QString((line_event->event_type == GPIOD_LINE_EVENT_RISING_EDGE) ? "rising" : "falling") + << " ts=" << timestamp.time_since_epoch().count(); + } } -void PigpiodHandler::setPullDown(unsigned int gpio) +void PigpiodHandler::eventHandler(struct gpiod_line* line) { - if (isInitialised) - set_pull_up_down(pi, gpio, PI_PUD_DOWN); -} + static constexpr struct timespec timeout { + 4, 0 + }; + while (fThreadRunning) { + if (inhibit) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } -void PigpiodHandler::setGpioState(unsigned int gpio, bool state) -{ - if (isInitialised) { - gpio_write(pi, gpio, (state) ? 1 : 0); + const unsigned int gpio = gpiod_line_offset(line); + std::shared_ptr line_event { std::make_shared() }; + int ret = gpiod_line_event_wait(line, &timeout); + if (ret > 0) { + int read_result = gpiod_line_event_read(line, line_event.get()); + if (read_result == 0) { + std::thread process_event_bg(&PigpiodHandler::processEvent, this, gpio, std::move(line_event)); + process_event_bg.detach(); + } else { + // an error occured + // what should we do here? + qCritical() << "read gpio line event failed"; + } + } else if (ret == 0) { + // a timeout occurred, no event was detected + // simply go over into the wait loop again + } else { + // an error occured + // what should we do here? + qCritical() << "wait for gpio line event failed"; + } } } -void PigpiodHandler::registerForCallback(unsigned int gpio, bool edge) +bool PigpiodHandler::setPinInput(unsigned int gpio) { - int result = callback(pi, gpio, edge ? FALLING_EDGE : RISING_EDGE, cbFunction); - if (result < 0) { - GPIO_SIGNAL pin = bcmToGpioSignal(gpio); - qCritical() << "error registering gpio callback for BCM pin" << QString::fromStdString(GPIO_SIGNAL_MAP.at(pin).name); + if (!isInitialised) + return false; + auto it = fLineMap.find(gpio); + + if (it != fLineMap.end()) { + // line object exists, request for input + int ret = gpiod_line_request_input(it->second, CONSUMER); + if (ret < 0) { + qCritical() << "Request gpio line" << gpio << "as input failed"; + return false; + } + return true; } + // line was not allocated yet, so do it now + gpiod_line* line = gpiod_chip_get_line(fChip, gpio); + if (line == nullptr) { + qCritical() << "error allocating gpio line" << gpio; + return false; + } + int ret = gpiod_line_request_input(line, CONSUMER); + if (ret < 0) { + qCritical() << "Request gpio line" << gpio << "as input failed"; + return false; + } + fLineMap.emplace(std::make_pair(gpio, line)); + return true; } -void PigpiodHandler::writeSpi(uint8_t command, std::string data) +bool PigpiodHandler::setPinOutput(unsigned int gpio, bool initState) { - if (!spiInitialised) { - if (!spiInitialise()) { - return; + if (!isInitialised) + return false; + auto it = fLineMap.find(gpio); + + if (it != fLineMap.end()) { + // line object exists, request for output + int ret = gpiod_line_request_output(it->second, CONSUMER, static_cast(initState)); + if (ret < 0) { + qCritical() << "Request gpio line" << gpio << "as output failed"; + return false; } + return true; } - - char* txBuf { static_cast(calloc(sizeof(char), data.size() + 1)) }; - txBuf[0] = (char)command; - for (unsigned int i = 1; i < data.size() + 1; i++) { - txBuf[i] = data[i - 1]; + // line was not allocated yet, so do it now + gpiod_line* line = gpiod_chip_get_line(fChip, gpio); + if (line == nullptr) { + qCritical() << "error allocating gpio line" << gpio; + return false; } - - char* rxBuf { static_cast(calloc(sizeof(char), data.size() + 1)) }; - if (spi_xfer(pi, spiHandle, txBuf, rxBuf, data.size() + 1) != static_cast(1 + data.size())) { - qWarning() << "writeSpi(uint8_t, std::string): wrong number of bytes transfered"; + int ret = gpiod_line_request_output(line, CONSUMER, static_cast(initState)); + if (ret < 0) { + qCritical() << "Request gpio line" << gpio << "as output failed"; + return false; } - free(txBuf); - free(rxBuf); + fLineMap.emplace(std::make_pair(gpio, line)); + return true; } -void PigpiodHandler::readSpi(uint8_t command, unsigned int bytesToRead) +bool PigpiodHandler::setPinBias(unsigned int gpio, std::uint8_t bias_flags) { - if (!spiInitialised) { - if (!spiInitialise()) { - return; - } + if (!isInitialised) + return false; + int flags = 0; + if (bias_flags & PinBias::OpenDrain) { + flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN; + } else if (bias_flags & PinBias::OpenSource) { + flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE; + } else if (bias_flags & PinBias::ActiveLow) { + flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + } else if (bias_flags & PinBias::PullDown) { + //flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN; + } else if (bias_flags & PinBias::PullUp) { + //flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP; + } else if (!(bias_flags & PinBias::PullDown) && !(bias_flags & PinBias::PullUp)) { + //flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLED; } - char* rxBuf { static_cast(calloc(sizeof(char), bytesToRead + 1)) }; - char* txBuf { static_cast(calloc(sizeof(char), bytesToRead + 1)) }; - - txBuf[0] = (char)command; - for (unsigned int i = 1; i < bytesToRead; i++) { - txBuf[i] = 0; + auto it = fLineMap.find(gpio); + int dir = -1; + if (it != fLineMap.end()) { + // line object exists, set config + dir = gpiod_line_direction(it->second); + gpiod_line_release(it->second); + fLineMap.erase(it); } - if (spi_xfer(pi, spiHandle, txBuf, rxBuf, bytesToRead + 1) != static_cast(1 + bytesToRead)) { - qWarning() << "readSpi(uint8_t, unsigned int): wrong number of bytes transfered"; - free(txBuf); - free(rxBuf); - return; + // line was not allocated yet, so do it now + gpiod_line* line = gpiod_chip_get_line(fChip, gpio); + if (line == nullptr) { + qCritical() << "error allocating gpio line" << gpio; + return false; } - - std::string data; - for (unsigned int i = 1; i < bytesToRead + 1; i++) { - data += rxBuf[i]; + int req_mode = GPIOD_LINE_REQUEST_DIRECTION_AS_IS; + if (dir == GPIOD_LINE_DIRECTION_INPUT) + req_mode = GPIOD_LINE_REQUEST_DIRECTION_INPUT; + else if (dir == GPIOD_LINE_DIRECTION_OUTPUT) + req_mode = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; + gpiod_line_request_config config { CONSUMER, req_mode, flags }; + int ret = gpiod_line_request(line, &config, 0); + if (ret < 0) { + qCritical() << "Request gpio line" << gpio << "for bias config failed"; + return false; } - - free(txBuf); - free(rxBuf); - - emit spiData(command, data); + fLineMap.emplace(std::make_pair(gpio, line)); + return true; } -bool PigpiodHandler::initialised() +bool PigpiodHandler::setPinState(unsigned int gpio, bool state) { - return isInitialised; + if (!isInitialised) + return false; + auto it = fLineMap.find(gpio); + if (it != fLineMap.end()) { + // line object exists, request for output + int ret = gpiod_line_set_value(it->second, static_cast(state)); + if (ret < 0) { + qCritical() << "Setting state of gpio line" << gpio << "failed"; + return false; + } + return true; + } + // line was not allocated yet, so do it now + return setPinOutput(gpio, state); } -bool PigpiodHandler::isSpiInitialised() +void PigpiodHandler::reloadInterruptSettings() { - return spiInitialised; + this->stop(); + this->start(); } -bool PigpiodHandler::spiInitialise() +bool PigpiodHandler::registerInterrupt(unsigned int gpio, EventEdge edge) { - if (!isInitialised) { - qCritical() << "pigpiohandler not initialised"; + if (!isInitialised) return false; - } - if (spiInitialised) { + auto it = fInterruptLineMap.find(gpio); + if (it != fInterruptLineMap.end()) { + // line object exists + // The following code block shall release the previous line request + // and re-request this line for events. + // It is bypassed, since it does not work as intended. + // The function simply does nothing in this case. + return false; + //stop(); + //gpiod_line_release(it->second); + if (gpiod_line_update(it->second) < 0) { + qCritical() << "update of gpio line" << gpio << "after release failed"; + //return false; + } + // request for events + int ret = -1; + int errcnt = 10; + while (errcnt && ret < 0) { + switch (edge) { + case EventEdge::RisingEdge: + ret = gpiod_line_request_rising_edge_events(it->second, CONSUMER); + break; + case EventEdge::FallingEdge: + ret = gpiod_line_request_falling_edge_events(it->second, CONSUMER); + break; + case EventEdge::BothEdges: + ret = gpiod_line_request_both_edges_events(it->second, CONSUMER); + default: + break; + } + errcnt--; + if (ret < 0) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + if (ret < 0) { + qCritical() << "Re-request gpio line" << gpio << "for events failed"; + return false; + } return true; } - spiHandle = spi_open(pi, 0, spiClkFreq, spiFlags); - if (spiHandle < 0) { - QString errstr = ""; - switch (spiHandle) { - case PI_BAD_CHANNEL: - errstr = "DMA channel not 0-15"; - break; - case PI_BAD_SPI_SPEED: - errstr = "bad SPI speed"; - break; - case PI_BAD_FLAGS: - errstr = "bad spi open flags"; - break; - case PI_NO_AUX_SPI: - errstr = "no auxiliary SPI on Pi A or B"; - break; - case PI_SPI_OPEN_FAILED: - errstr = "can't open SPI device"; - break; - default: - break; - } - qCritical() << "Could not initialise SPI bus:" << errstr; + // line was not allocated yet, so do it now + gpiod_line* line = gpiod_chip_get_line(fChip, gpio); + if (line == nullptr) { + qCritical() << "error allocating gpio line" << gpio; return false; } - spiInitialised = true; + int ret = -1; + switch (edge) { + case EventEdge::RisingEdge: + ret = gpiod_line_request_rising_edge_events(line, CONSUMER); + break; + case EventEdge::FallingEdge: + ret = gpiod_line_request_falling_edge_events(line, CONSUMER); + break; + case EventEdge::BothEdges: + ret = gpiod_line_request_both_edges_events(line, CONSUMER); + default: + break; + } + if (ret < 0) { + qCritical() << "Request gpio line" << gpio << "for events failed"; + return false; + } + fInterruptLineMap.emplace(std::make_pair(gpio, line)); + reloadInterruptSettings(); return true; } +bool PigpiodHandler::unRegisterInterrupt(unsigned int gpio) +{ + if (!isInitialised) + return false; + auto it = fInterruptLineMap.find(gpio); + if (it != fInterruptLineMap.end()) { + gpiod_line_release(it->second); + fInterruptLineMap.erase(it); + reloadInterruptSettings(); + return true; + } + return false; +} + +bool PigpiodHandler::initialised() +{ + return isInitialised; +} + void PigpiodHandler::stop() { - if (!isInitialised) { + fThreadRunning = false; + for (auto& [gpio, line_thread] : fThreads) { + if (line_thread) + line_thread->join(); + } +} + +void PigpiodHandler::start() +{ + if (fThreadRunning) return; + fThreadRunning = true; + + for (auto& [gpio, line] : fInterruptLineMap) { + fThreads[gpio] = std::make_unique([this, gpio, line]() { this->eventHandler(line); }); } - isInitialised = false; - pigpio_stop(pi); - pigHandlerAddress.clear(); } void PigpiodHandler::measureGpioClockTime() @@ -368,7 +425,8 @@ void PigpiodHandler::measureGpioClockTime() if (!isInitialised) return; static uint32_t oldTick = 0; - const int N = MuonPi::Config::Hardware::GPIO::Clock::Measurement::buffer_size; + // static uint64_t llTick = 0; + const int N = MuonPi::Config::Hardware::GPIO::Clock::Measurement::buffer_size /*GPIO_CLOCK_MEASUREMENT_BUFFER_SIZE*/; static int nrSamples = 0; static int arrayIndex = 0; static qint64 diff_array[N]; @@ -378,8 +436,10 @@ void PigpiodHandler::measureGpioClockTime() quint64 t0 = startOfProgram.toMSecsSinceEpoch(); clock_gettime(CLOCK_REALTIME, &tp1); - uint32_t tick = get_current_tick(pi); + /// uint32_t tick = get_current_tick(pi); + uint32_t tick = 0; clock_gettime(CLOCK_REALTIME, &tp2); + // clock_gettime(CLOCK_MONOTONIC, &tp); qint64 dt = tp2.tv_sec - tp1.tv_sec; dt *= 1000000000LL; @@ -408,4 +468,6 @@ void PigpiodHandler::measureGpioClockTime() calcLinearCoefficients(nrSamples, tick_array, diff_array, &offs, &slope, arrayIndex); clockMeasurementSlope = slope; clockMeasurementOffset = offs; + + // qDebug() << "gpio clock measurement: N=" << nrSamples << " offs=" << offs << " slope=" << slope; } diff --git a/daemon/src/utility/filehandler.cpp b/daemon/src/utility/filehandler.cpp index b53a32f4..5d166ac5 100644 --- a/daemon/src/utility/filehandler.cpp +++ b/daemon/src/utility/filehandler.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -11,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -327,18 +327,17 @@ bool FileHandler::rotateFiles() currentWorkingLogPath = dataFolderPath + "log_" + fileNamePart; auto currentWorkingFilePathCandidate = currentWorkingFilePath; auto currentWorkingLogPathCandidate = currentWorkingLogPath; - for (size_t i=0; i<1000ul; i++){ - if ( QFile::exists(currentWorkingFilePathCandidate) || QFile::exists(currentWorkingLogPathCandidate)){ - qDebug()< -constexpr auto invalid_time = std::chrono::system_clock::time_point::min(); +constexpr auto invalid_time = TimestampClockType::time_point::min(); CounterRateBuffer::CounterRateBuffer(unsigned int counter_mask, QObject* parent) : QObject(parent) , m_counter_mask(counter_mask) - , m_instance_start(std::chrono::system_clock::now()) + , m_instance_start(TimestampClockType::now()) { } void CounterRateBuffer::onCounterValue(uint16_t value) { - EventTime event_time { std::chrono::system_clock::now() }; + EventTime event_time { TimestampClockType::now() }; m_countbuffer.emplace_back(event_time, value); if (m_countbuffer.size() == 1) { return; @@ -29,7 +29,7 @@ auto CounterRateBuffer::avgRate() const -> double if (m_countbuffer.size() < 2) { return 0.; } - auto tend = std::chrono::system_clock::now(); + auto tend = TimestampClockType::now(); auto tstart = tend - m_buffer_time; if (tstart < m_instance_start) { tstart = m_instance_start; @@ -58,21 +58,21 @@ auto CounterRateBuffer::avgRate() const -> double EventRateBuffer::EventRateBuffer(unsigned int gpio, QObject* parent) : QObject(parent) , m_gpio(gpio) - , m_instance_start(std::chrono::system_clock::now()) + , m_instance_start(TimestampClockType::now()) { } void EventRateBuffer::clear() { m_eventbuffer = std::queue> {}; - m_instance_start = std::chrono::system_clock::now(); + m_instance_start = TimestampClockType::now(); } -void EventRateBuffer::onEvent(uint8_t gpio) +void EventRateBuffer::onEvent(unsigned int gpio, EventTime event_time) { if (gpio != m_gpio) return; - EventTime event_time { std::chrono::system_clock::now() }; + //EventTime event_time { TimestampClockType::now() }; if (m_eventbuffer.empty()) { m_eventbuffer.push(event_time); emit filteredEvent(gpio, event_time); @@ -81,6 +81,7 @@ void EventRateBuffer::onEvent(uint8_t gpio) auto last_event_time = m_eventbuffer.back(); if (event_time - last_event_time < m_current_deadtime) { + emit eventIntervalSignal(gpio, event_time - last_event_time); // m_buffer[gpio].eventbuffer.push(event_time); return; } @@ -92,14 +93,15 @@ void EventRateBuffer::onEvent(uint8_t gpio) if (!m_eventbuffer.empty()) { m_last_interval = std::chrono::duration_cast(event_time - last_event_time); - if (event_time - last_event_time < MAX_DEADTIME) { + if (m_last_interval < MAX_DEADTIME) { // std::cout << "now-last:"<<(now-last_event_time)/1us<<" dt="< double { if (m_eventbuffer.empty()) return 0.; - auto tend = std::chrono::system_clock::now(); + auto tend = TimestampClockType::now(); auto tstart = tend - m_buffer_time; if (tstart < m_instance_start) tstart = m_instance_start; diff --git a/gui/config/changelog b/gui/config/changelog index bef524f3..78a09cd9 100644 --- a/gui/config/changelog +++ b/gui/config/changelog @@ -1,7 +1,7 @@ muondetector-gui (2.1.0) ; urgency=low * Updates: - - Intensively improved the geographic map feature with position marker (zoom level independent), semitransparent accuracy indicator and info box with current coordinates and position accuracy. + - Extended the geographic map feature with position marker (zoom level independent), semitransparent accuracy indicator and info box with current coordinates and position accuracy. - Added daemon hardware and software info under "Parameters" tab. - Added time as scan parameter in "Scans" tab. - Added display of log rotation interval and event log enable flag in "Log" tab. @@ -11,7 +11,7 @@ muondetector-gui (2.1.0) ; urgency=low - Parameters: Added daemon hardware and software information - Scans: Added time as scan parameter - Log: Added display of log rotation interval and event log enable flag - - Map: Intensively improved the geographic map + - Map: Extensively improved the geographic map - position marker (zoom level independent) - semitransparent accuracy indicator - info box with current coordinates and position accuracy diff --git a/gui/src/mainwindow.cpp b/gui/src/mainwindow.cpp index 233dd06d..d056a689 100644 --- a/gui/src/mainwindow.cpp +++ b/gui/src/mainwindow.cpp @@ -83,7 +83,7 @@ MainWindow::MainWindow(QWidget* parent) if (addresses == nullptr) { return; } - unsigned device_counter{}; + unsigned device_counter {}; for (auto device : devices) { // check if device is not a GUI (might show other GUIs later on) if (device.first == static_cast(NetworkDiscovery::DeviceType::GUI)) { @@ -992,8 +992,8 @@ void MainWindow::connected() void MainWindow::connection_info(const QString message) { - ui->ipStatusLabel->setStyleSheet(""); - ui->ipStatusLabel->setText(message); + ui->ipStatusLabel->setStyleSheet(""); + ui->ipStatusLabel->setText(message); } void MainWindow::connection_error(int error_code, const QString message) diff --git a/library/include/gpio_pin_definitions.h b/library/include/gpio_pin_definitions.h index fc3d264e..c17f07c5 100644 --- a/library/include/gpio_pin_definitions.h +++ b/library/include/gpio_pin_definitions.h @@ -1,18 +1,15 @@ -#ifndef GPIO_PIN_DEFINITIONS_H -#define GPIO_PIN_DEFINITIONS_H +#ifndef GPIO_SIGNAL_DEFINITIONS_H +#define GPIO_SIGNAL_DEFINITIONS_H #include "muondetector_shared_global.h" #include #include -// define the pins which are used to interface the raspberry pi -// UBIAS_EN is the power on/off pin for bias voltage -// PREAMP_1/2 enables the DC voltage to power the preamp through the signal cable -// EVT_AND, EVT_XOR are the event inputs from AND and XOR gates +// Define the signals of the hardware interface to the MuonPi HAT // Note: The pin definitions are enum constants and have nothing to do with the actual pin numbers // of the RPi GPIO header. To be independent of the specific hardware implementation, -// the pin numbers for these signals are defined in gpio_pin_mapping.h on the daemon side +// the pin numbers for these signals are defined in gpio_pin_mapping.h on the daemon side through the static map GPIO_PINMAP enum GPIO_SIGNAL { UBIAS_EN, PREAMP_1, @@ -32,7 +29,7 @@ enum GPIO_SIGNAL { UBIAS_EN, TDC_STATUS, IN_POL1, IN_POL2, - UNDEFINED_PIN = 255 + UNDEFINED_SIGNAL = 255 }; enum SIGNAL_DIRECTION { DIR_UNDEFINED, @@ -63,7 +60,7 @@ static const std::map GPIO_SIGNAL_MAP = { { U { TDC_STATUS, { "TDC_STATUS", DIR_OUT } }, { IN_POL1, { "IN_POL1", DIR_OUT } }, { IN_POL2, { "IN_POL2", DIR_OUT } }, - { UNDEFINED_PIN, { "UNDEFINED_PIN", DIR_UNDEFINED } } }; + { UNDEFINED_SIGNAL, { "UNDEFINED_SIGNAL", DIR_UNDEFINED } } }; enum class TIMING_MUX_SELECTION : uint8_t { AND = 0, @@ -89,4 +86,4 @@ static const std::map TIMING_MUX_SIGNAL_NAMES { TIMING_MUX_SELECTION::UNDEFINED, "UNDEFINED" } }; -#endif // GPIO_PIN_DEFINITIONS_H +#endif // GPIO_SIGNAL_DEFINITIONS_H diff --git a/library/include/muondetector_structs.h b/library/include/muondetector_structs.h index b1c5759e..bdea11e1 100644 --- a/library/include/muondetector_structs.h +++ b/library/include/muondetector_structs.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,8 @@ #include #include -using EventTime = std::chrono::time_point; +using TimestampClockType = std::chrono::steady_clock; +using EventTime = std::chrono::time_point; enum class ADC_SAMPLING_MODE { DISABLED = 0, @@ -104,7 +106,7 @@ struct PositionModeConfig { struct IoConfiguration { TIMING_MUX_SELECTION timing_input { TIMING_MUX_SELECTION::UNDEFINED }; - GPIO_SIGNAL event_trigger { GPIO_SIGNAL::UNDEFINED_PIN }; + GPIO_SIGNAL event_trigger { GPIO_SIGNAL::UNDEFINED_SIGNAL }; GeneralEvent led1_event; GeneralEvent led2_event; }; diff --git a/library/include/networkdiscovery.h b/library/include/networkdiscovery.h index b4f537b4..03b6089e 100644 --- a/library/include/networkdiscovery.h +++ b/library/include/networkdiscovery.h @@ -3,8 +3,8 @@ #include #include -#include #include +#include class NetworkDiscovery : public QObject { Q_OBJECT diff --git a/library/src/networkdiscovery.cpp b/library/src/networkdiscovery.cpp index 0d04ec58..8f2af40f 100644 --- a/library/src/networkdiscovery.cpp +++ b/library/src/networkdiscovery.cpp @@ -43,8 +43,7 @@ void NetworkDiscovery::searchDevices() // auto datagram = QNetworkDatagram{data,m_broadcast_address, m_port}; // datagram.setHopLimit(255); // probably overkill qDebug() << "NetworkDiscovery is an experimental feature and may or may not work!"; - for (auto address : m_broadcast_address) - { + for (auto address : m_broadcast_address) { qDebug() << "NetworkDiscovery: sending " << data << " on address " << QHostAddress(address.toIPv4Address()); socket->writeDatagram(data, address, m_port); } @@ -74,15 +73,15 @@ void NetworkDiscovery::readPendingDatagrams() if (device_type == static_cast(m_device_type)) { bool skip = false; for (auto address : m_own_ipv4) { - if (address == sender_address){ + if (address == sender_address) { skip = true; } } - if (skip){ + if (skip) { continue; // do not answer or discover self } } - discovered_devices.append(QPair { static_cast(device_type), sender_address}); + discovered_devices.append(QPair { static_cast(device_type), sender_address }); if (static_cast(device_type) == DeviceType::GUI) { data = QByteArray();