Program Listing for File nlx.cpp

Return to documentation for file (lib/neuralynx/nlx.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 <http://www.gnu.org/licenses/>.
// ---------------------------------------------------------------------

#include "nlx.hpp"
#include <algorithm>
#include <cassert>
#include <iostream>

using namespace nlx;

bool valid_nlx_vt(VideoRec *vt_record, std::uint16_t vt_id,
                  ErrorNLXVT::Code &error_code,
                  decltype(NLX_VIDEO_RESOLUTION) resolution) {
  error_code = ErrorNLXVT::Code::NO_ERROR;
  if (vt_record->swstx != VTRecSWST) {
    error_code = ErrorNLXVT::Code::SWSTX;
    return false;
  }
  if (vt_record->swid != vt_id) {
    error_code = ErrorNLXVT::Code::SWID;
    return false;
  }
  if (sizeof(VideoRec) != vt_record->swdata_size) {
    error_code = ErrorNLXVT::Code::SWDATA_SIZE;
    return false;
  }
  if ((vt_record->dnextracted_x < 0) || (vt_record->dnextracted_y < 0)) {
    error_code = ErrorNLXVT::Code::NEGATIVE_COORDINATE;
    return false;
  }
  if (vt_record->dnextracted_x >= resolution[0] ||
      vt_record->dnextracted_y >= resolution[1]) {
    error_code = ErrorNLXVT::Code::OUT_OF_RESOLUTION;
    return false;
  }
  return true;
}

NlxSignalRecord::NlxSignalRecord(unsigned int nchannels,
                                 bool convert_byte_order)
    : convert_byte_order_(convert_byte_order) {
  set_nchannels(nchannels);
}

unsigned int NlxSignalRecord::nchannels() const { return nchannels_; }

void NlxSignalRecord::set_nchannels(unsigned int n) {
  nchannels_ = n;

  nlx_nfields_ = NLX_NFIELDS(nchannels_);
  nlx_packetbytesize_ = NLX_PACKETBYTESIZE(nchannels_);
  nlx_field_crc_ = nlx_field_crc(nchannels_);
  nlx_field_data_last_ = nlx_field_data_last(nchannels_);
  nlx_packetsize_ = nlx_packetsize(nchannels_);

  buffer_.resize(nlx_nfields_);
  Initialize();
}

bool NlxSignalRecord::convert_byte_order() const { return convert_byte_order_; }

void NlxSignalRecord::set_convert_byte_order(bool b) {
  convert_byte_order_ = b;
}

int NlxSignalRecord::FromNetworkBuffer(const char *buffer, size_t n) {
  // check size
  if (n != nlx_packetbytesize_) {
    return ERROR_TOO_SMALL_PACKET;
  }

  std::copy(buffer, buffer + n, (char *)buffer_.data());

  std::uint32_t tmp = ((NLX_STX << 8) & 0xFF00FF00) | ((NLX_STX >> 8) & 0xFF00FF);

  if (buffer_[NLX_FIELD_STX] == tmp){
    char *p = (char *)buffer_.data();
    for (unsigned int k = 0; k < n; k += 2) {
      set_convert_byte_order(true);
      *((uint16_t *)(p + k)) = ntohs(*((uint16_t *)(buffer + k)));
    }
  }

  // test if valid record (record size os OK, first 3 fields are OK, CRC checks
  // out)
  return valid(buffer_);
}

size_t NlxSignalRecord::ToNetworkBuffer(char *buffer, size_t n) {
  // check size
  if (n < nlx_packetbytesize_) {
    return 0;
  }

  // finalize if necessary
  if (!finalized_) {
    Finalize();
  }

  char *p = (char *)buffer_.data();

  if (convert_byte_order_) {
    // perform hton conversion, copying into provided buffer in the process
    for (unsigned int k = 0; k < nlx_packetbytesize_; k += 2) {
      *((uint16_t *)(buffer + k)) = htons(*((uint16_t *)(p + k)));
    }
  } else {
    std::copy(p, p + n, buffer);
  }

  return nlx_packetbytesize_;
}

void NlxSignalRecord::Initialize() {
  std::fill(buffer_.begin(), buffer_.end(), 0);
  buffer_[NLX_FIELD_STX] = NLX_STX;
  buffer_[NLX_FIELD_RAWPACKETID] = NLX_RAWPACKETID;
  buffer_[NLX_FIELD_PACKETSIZE] = nlx_packetsize(nchannels_);
  initialized_ = true;
}

void NlxSignalRecord::Finalize() {
  buffer_[nlx_field_crc_] = crc();
  finalized_ = true;
}

int32_t NlxSignalRecord::crc() const {
  int32_t c = 0;
  for (unsigned int k = 0; k < nlx_nfields_ - 1; ++k) {
    c ^= buffer_[k];
  }
  return c;
}

bool NlxSignalRecord::initialized() const { return initialized_; }

bool NlxSignalRecord::finalized() const { return finalized_; }

int NlxSignalRecord::valid(std::vector<int32_t> buffer) {

  if (buffer[NLX_FIELD_STX] != NLX_STX){
    initialized_ = false;
    return ERROR_NLX_FIELD_STX;
  }
  else if(buffer[NLX_FIELD_RAWPACKETID] != NLX_RAWPACKETID){
    initialized_ = false;
    return ERROR_NLX_FIELD_RAWPACKETID;
  }
  else if(buffer[NLX_FIELD_PACKETSIZE] != nlx_packetsize_) {
    initialized_ = false;
    return ERROR_NLX_FIELD_PACKETSIZE;
  }

  if (buffer[nlx_field_crc_] != crc()) {
    finalized_ = false;
    return ERROR_BAD_CRC;
  }

  initialized_ = true;
  finalized_ = true;

  return SUCCESS_READING_BUFFER;
}

uint64_t NlxSignalRecord::timestamp() const {
  uint64_t t;
  t = (uint32_t)buffer_[NLX_FIELD_TIMESTAMP_HIGH];
  t = (t << 32) + (uint32_t)buffer_[NLX_FIELD_TIMESTAMP_LOW];
  return t;
}
void NlxSignalRecord::set_timestamp(uint64_t t) {
  buffer_[NLX_FIELD_TIMESTAMP_HIGH] = (int32_t)(t >> 32);
  buffer_[NLX_FIELD_TIMESTAMP_LOW] = (int32_t)t;
  finalized_ = false;
}
void NlxSignalRecord::inc_timestamp(uint64_t delta) {
  set_timestamp(timestamp() + delta);
}
void NlxSignalRecord::inc_timestamp(double delta) {
  set_timestamp(
      timestamp() +
      static_cast<uint64_t>(1000000 * delta / NLX_SIGNAL_SAMPLING_FREQUENCY));
}

uint32_t NlxSignalRecord::parallel_port() const {
  return static_cast<uint32_t>(buffer_[NLX_FIELD_DIO]);
}
void NlxSignalRecord::set_parallel_port(uint32_t dio) {
  buffer_[NLX_FIELD_DIO] = dio;
  finalized_ = false;
}

void NlxSignalRecord::data(std::vector<int32_t> &v) const {
  if (v.size() < nchannels_) {
    v.resize(nchannels_);
  }
  std::copy(data_begin(), data_end(), v.begin());
}

std::vector<int32_t>::iterator
NlxSignalRecord::data(std::vector<int32_t>::iterator it) const {
  return std::copy(data_begin(), data_end(), it);
}

int32_t NlxSignalRecord::sample(unsigned int index) const {
  return buffer_[NLX_FIELD_DATA_FIRST + index];
}

void NlxSignalRecord::set_data(int32_t value) {
  std::fill(data_begin(), data_end(), value);
  finalized_ = false;
}

void NlxSignalRecord::set_data(std::vector<int32_t> &v) {
  assert(v.size() >= nchannels_);
  std::copy(v.begin(), v.begin() + nchannels_, data_begin());
  finalized_ = false;
}

void NlxSignalRecord::set_data(std::vector<int32_t>::iterator it) {
  std::copy(it, it + nchannels_, data_begin());
  finalized_ = false;
}

void NlxSignalRecord::data(std::vector<double> &v) const {
  if (v.size() < nchannels_) {
    v.resize(nchannels_);
  }
  data(v.begin());
}
std::vector<double>::iterator
NlxSignalRecord::data(std::vector<double>::iterator it) const {
  auto first = data_begin();
  auto last = data_end();
  while (first != last) {
    *it = (double)*first * NLX_AD_BIT_MICROVOLTS;
    ++it;
    ++first;
  }
  return it;
}

double NlxSignalRecord::sample_microvolt(unsigned int index) const {
  return static_cast<double>(buffer_[NLX_FIELD_DATA_FIRST + index] *
                             NLX_AD_BIT_MICROVOLTS);
}

void NlxSignalRecord::set_data(double value) {
  int32_t v = static_cast<int32_t>(value / NLX_AD_BIT_MICROVOLTS);
  std::fill(data_begin(), data_end(), v);
  finalized_ = false;
}

void NlxSignalRecord::set_data(std::vector<double> &v) {
  assert(v.size() >= nchannels_);
  set_data(v.begin());
}

void NlxSignalRecord::set_data(std::vector<double>::iterator it) {
  auto first = data_begin();
  for (unsigned int c = 0; c < nchannels_; ++c) {
    *first = static_cast<int32_t>(*it / NLX_AD_BIT_MICROVOLTS);
    ++first;
    ++it;
  }
  finalized_ = false;
}

std::vector<int32_t>::iterator NlxSignalRecord::data_begin() {
  return buffer_.begin() + NLX_FIELD_DATA_FIRST;
}

std::vector<int32_t>::iterator NlxSignalRecord::data_end() {
  return buffer_.begin() + nlx_field_data_last_ + 1;
}

std::vector<int32_t>::const_iterator NlxSignalRecord::data_begin() const {
  return buffer_.begin() + NLX_FIELD_DATA_FIRST;
}

std::vector<int32_t>::const_iterator NlxSignalRecord::data_end() const {
  return buffer_.begin() + nlx_field_data_last_ + 1;
}

void NlxStatistics::clear() {
  n_invalid = 0;
  n_duplicated = 0;
  n_outoforder = 0;
  n_missed = 0;
  n_gaps = 0;
}

uint64_t nlx::CheckTimestamp(const NlxSignalRecord &rec,
                             uint64_t &last_timestamp, NlxStatistics &stats) {
  uint64_t timestamp = rec.timestamp();

  if (last_timestamp == nlx::INVALID_TIMESTAMP) {
    last_timestamp = timestamp;
  } else if (timestamp == last_timestamp) {
    ++stats.n_duplicated;
  } else if (timestamp < last_timestamp) {
    ++stats.n_outoforder;
  } else {
    uint64_t delta = timestamp - last_timestamp;
    if (delta > nlx::MAX_ALLOWABLE_TIMEGAP_MICROSECONDS) {
      int64_t n_missed = round(delta / nlx::SAMPLING_PERIOD_MICROSEC) - 1;
      stats.n_missed += n_missed;
      ++stats.n_gaps;
      // LOG(DEBUG) << n_missed << " timestamps were found to be missing. ";
    }
    last_timestamp = timestamp;
  }

  return timestamp;
}