Program Listing for File nlx.hpp

Return to documentation for file (lib/neuralynx/nlx.hpp)

// ---------------------------------------------------------------------
// 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 <http://www.gnu.org/licenses/>.
// ---------------------------------------------------------------------

#pragma once

#include <netinet/in.h>

#include <array>
#include <cmath>
#include <cstdint>
#include <vector>
#include <limits>

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<std::int32_t, 2> 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 <typename T> int FromNetworkBuffer(const std::vector<T> &buffer) {
    return FromNetworkBuffer((char *)buffer.data(), buffer.size() * sizeof(T));
  }

  size_t ToNetworkBuffer(char *buffer, size_t n);

  template <typename T> size_t ToNetworkBuffer(std::vector<T> &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<int32_t> 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<int32_t> &v) const;  // will copy
  std::vector<int32_t>::iterator
  data(std::vector<int32_t>::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<int32_t> &v);
  void set_data(std::vector<int32_t>::iterator it);

  // data (microVolt) getter methods
  void data(std::vector<double> &v) const;  // will convert to uV and copy
  std::vector<double>::iterator data(std::vector<double>::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<double> &v);
  void set_data(std::vector<double>::iterator it);

  std::vector<int32_t> buffer_;
  int32_t nlx_packetsize_;
protected:
  std::vector<int32_t>::iterator data_begin();
  std::vector<int32_t>::iterator data_end();

  std::vector<int32_t>::const_iterator data_begin() const;
  std::vector<int32_t>::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<uint64_t>::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