.. _program_listing_file_lib_neuralynx_nlx.hpp: Program Listing for File nlx.hpp ================================ |exhale_lsh| :ref:`Return to documentation for file ` (``lib/neuralynx/nlx.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp // --------------------------------------------------------------------- // This file is part of falcon-core. // // Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders // // Falcon-server is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Falcon-server is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with falcon-core. If not, see . // --------------------------------------------------------------------- #pragma once #include #include #include #include #include #include namespace nlx { // a digilynx raw packet has the following layout // int32 | start of transmission identifier == 2048 // int32 | packet type identifier == 1 // int32 | packet size == #channels + #extra // uint32 | timestamp (high order bits) // uint32 | timestamp (low order bits) // int32 | status (reserved) // uint32 | parallel input port value // int32[10] | extra values (reserved) // int32[nchannels] | data values (nchannels == number of channels in // acquisition system) int32 | CRC value for record // some values are needed at compile time constexpr uint16_t NLX_NFIELDS_EXTRA = 10; constexpr uint16_t NLX_FIELDBYTESIZE = 4; constexpr uint16_t NLX_NFIELDS(uint16_t c) { return (8 + NLX_NFIELDS_EXTRA + (c)); } constexpr uint16_t NLX_PACKETBYTESIZE(uint16_t c) { return (NLX_FIELDBYTESIZE * NLX_NFIELDS(c)); } constexpr uint16_t NLX_NCHANNELS_FROM_PACKETBYTESIZE(uint16_t sz) { return ((sz / NLX_FIELDBYTESIZE) - 8 - NLX_NFIELDS_EXTRA); } constexpr uint16_t NLX_NCHANNELS_FROM_NFIELDS(uint16_t n) { return n - 8 - NLX_NFIELDS_EXTRA; } constexpr uint16_t SUCCESS_READING_BUFFER = 0; constexpr uint16_t ERROR_NLX_FIELD_STX = 2; constexpr uint16_t ERROR_NLX_FIELD_RAWPACKETID = 3; constexpr uint16_t ERROR_NLX_FIELD_PACKETSIZE = 5; constexpr uint16_t ERROR_TOO_SMALL_PACKET = 4; constexpr uint16_t ERROR_BAD_CRC = 1; constexpr uint16_t NLX_FIELD_STX = 0; constexpr uint16_t NLX_FIELD_RAWPACKETID = 1; constexpr uint16_t NLX_FIELD_PACKETSIZE = 2; constexpr uint16_t NLX_FIELD_TIMESTAMP_HIGH = 3; constexpr uint16_t NLX_FIELD_TIMESTAMP_LOW = 4; constexpr uint16_t NLX_FIELD_DIO = 6; constexpr uint16_t NLX_FIELD_EXTRA_FIRST = 7; constexpr uint16_t NLX_FIELD_DATA_FIRST = NLX_FIELD_EXTRA_FIRST + NLX_NFIELDS_EXTRA; constexpr double NLX_AD_BIT_MICROVOLTS = 0.015624999960550667; constexpr int32_t NLX_STX = 2048; constexpr int32_t NLX_RAWPACKETID = 1; constexpr uint16_t NLX_DEFAULT_NCHANNELS = 128; constexpr uint16_t NLX_MAX_NCHANNELS = 1024; constexpr double NLX_SIGNAL_SAMPLING_FREQUENCY = 32000; constexpr unsigned int NLX_DEFAULT_BUFFERSIZE = NLX_PACKETBYTESIZE(NLX_DEFAULT_NCHANNELS); const double NLX_VIDEO_SAMPLING_FREQUENCY = 25; const int VTRecNumTransitionBitfields = 400; const int VTRecNumTargets = 50; const std::uint16_t VTRecSWST = 0x800; typedef struct { // from Neuralynx header Nlx_DataTypes.h std::uint16_t swstx; std::uint16_t swid; std::uint16_t swdata_size; std::uint64_t qwTimeStamp; std::uint32_t dwPoints[VTRecNumTransitionBitfields]; std::int16_t sncrc; std::int32_t dnextracted_x; std::int32_t dnextracted_y; std::int32_t dnextracted_angle; std::int32_t dntargets[VTRecNumTargets]; } __attribute__((__packed__)) VideoRec; // packing does not hurt perfomance on x64 CPUs // used here in order to guarantee sizeof(VideoRec) == swdata_size so that is // read correctly (otherwise it is NOT!) read more on: // https://attractivechaos.wordpress.com/2013/05/02/does-packed-struct-hurt-performance-on-x86_64/ struct ErrorNLXVT { // wrapped to avoid namespace conflict enum Code { UNKNOWN = -1, NO_ERROR = 0, SWSTX, SWID, SWDATA_SIZE, NEGATIVE_COORDINATE, OUT_OF_RESOLUTION }; }; const std::array NLX_VIDEO_RESOLUTION = {{720, 576}}; bool valid_nlx_vt(VideoRec *vt_record, std::uint16_t vt_id, ErrorNLXVT::Code &error_code, decltype(NLX_VIDEO_RESOLUTION) resolution); inline const unsigned int nlx_field_data_last(unsigned int nchannels = NLX_DEFAULT_NCHANNELS) { return NLX_FIELD_DATA_FIRST + nchannels - 1; } inline const unsigned int nlx_field_crc(unsigned int nchannels = NLX_DEFAULT_NCHANNELS) { return NLX_FIELD_DATA_FIRST + nchannels; } inline const int32_t nlx_packetsize(unsigned int nchannels = NLX_DEFAULT_NCHANNELS) { return nchannels + NLX_NFIELDS_EXTRA; } class NlxSignalRecord { public: NlxSignalRecord(unsigned int nchannels = NLX_DEFAULT_NCHANNELS, bool convert_byte_order = true); unsigned int nchannels() const; void set_nchannels(unsigned int n); bool convert_byte_order() const; void set_convert_byte_order(bool b); int FromNetworkBuffer(const char *buffer, size_t n); template int FromNetworkBuffer(const std::vector &buffer) { return FromNetworkBuffer((char *)buffer.data(), buffer.size() * sizeof(T)); } size_t ToNetworkBuffer(char *buffer, size_t n); template size_t ToNetworkBuffer(std::vector &buffer) { // check size if (buffer.size() < (nlx_packetbytesize_ / sizeof(T))) { buffer.resize(nlx_packetbytesize_ / sizeof(T)); } return ToNetworkBuffer((char *)buffer.data(), buffer.size() * sizeof(T)); } void Initialize(); // set required fields 1-3 void Finalize(); // compute CRC int32_t crc() const; bool initialized() const; bool finalized() const; int valid(std::vector buffer); // timestamp access functions uint64_t timestamp() const; void set_timestamp(uint64_t t); void inc_timestamp(uint64_t delta); void inc_timestamp(double delta); // digital input/output access methods uint32_t parallel_port() const; void set_parallel_port(uint32_t dio = 0); // data (int32) getter methods void data(std::vector &v) const; // will copy std::vector::iterator data(std::vector::iterator it) const; // will copy int32_t sample(unsigned int index) const; // data (int32) setter methods void set_data(int32_t value = 0); void set_data(std::vector &v); void set_data(std::vector::iterator it); // data (microVolt) getter methods void data(std::vector &v) const; // will convert to uV and copy std::vector::iterator data(std::vector::iterator it) const; double sample_microvolt(unsigned int index) const; // data (microVolt) setter methods void set_data(double value = 0); void set_data(std::vector &v); void set_data(std::vector::iterator it); std::vector buffer_; int32_t nlx_packetsize_; protected: std::vector::iterator data_begin(); std::vector::iterator data_end(); std::vector::const_iterator data_begin() const; std::vector::const_iterator data_end() const; bool convert_byte_order_; unsigned int nchannels_; bool initialized_ = false; bool finalized_ = false; unsigned int nlx_nfields_; unsigned int nlx_packetbytesize_; uint16_t nlx_field_crc_; uint16_t nlx_field_data_last_; }; // timestamp related constants // sampling period (microseconds) constexpr decltype(NLX_SIGNAL_SAMPLING_FREQUENCY) SAMPLING_PERIOD_MICROSEC = 1e6 / NLX_SIGNAL_SAMPLING_FREQUENCY; // maximum tolerated difference between two timestamps that // is not considered a gap with missing data packets const uint64_t MAX_ALLOWABLE_TIMEGAP_MICROSECONDS = trunc(SAMPLING_PERIOD_MICROSEC) + 1; // value for an invalid timestamp constexpr uint64_t INVALID_TIMESTAMP = std::numeric_limits::max(); class NlxStatistics { public: NlxStatistics() : n_invalid(0), n_duplicated(0), n_outoforder(0), n_missed(0), n_gaps(0) { } public: uint64_t n_invalid; uint64_t n_duplicated; uint64_t n_outoforder; uint64_t n_missed; uint64_t n_gaps; void clear(); }; uint64_t CheckTimestamp(const NlxSignalRecord &rec, uint64_t &last_timestamp, NlxStatistics &stats); } // namespace nlx