Program Listing for File levelcrossingdetector.cpp

Return to documentation for file (processors/levelcrossingdetector/levelcrossingdetector.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 "levelcrossingdetector.hpp"

#include <limits>

LevelCrossingDetector::LevelCrossingDetector() : IProcessor() {
  add_option(THRESHOLD, initial_threshold_,
             "Threshold (in data units) that needs to be crossed.");
  add_option(
      UPSLOPE, initial_upslope_,
      "Either detect upward (true) or downward (false) threshold crossings.");
  add_option(POST_DETECT_BLOCK, initial_post_detect_block_,
             "Refractory period after threshold",
             "crossing detection (in number of samples).");
  add_option("event", event_prototype_,
             "The event to emit when the input signal crosses the threshold.");
}

void LevelCrossingDetector::CreatePorts() {
  data_in_port_ = create_input_port<MultiChannelType<double>>(
      "data", MultiChannelType<double>::Capabilities(ChannelRange(1, 256)),
      PortInPolicy(SlotRange(1)));

  data_out_port_ = create_output_port<EventType>(
      EVENTDATA, EventType::Capabilities(), EventType::Parameters(),
      PortOutPolicy(SlotRange(1)));

  threshold_ = create_static_state(THRESHOLD, initial_threshold_(), true,
                                   Permission::WRITE);

  upslope_ =
      create_static_state(UPSLOPE, initial_upslope_(), true, Permission::WRITE);

  post_detect_block_ = create_static_state(
      POST_DETECT_BLOCK, initial_post_detect_block_(), true, Permission::WRITE);
}

void LevelCrossingDetector::Preprocess(ProcessingContext &context) {
  double init_value;
  post_detection_block_update(initial_post_detect_block_());

  if (upslope_->get()) {
    init_value = std::numeric_limits<int>::max();
  } else {
    init_value = std::numeric_limits<int>::min();
  }

  previous_sample_.assign(data_in_port_->streaminfo(0).parameters().nchannels,
                          init_value);
}

void LevelCrossingDetector::Process(ProcessingContext &context) {
  double threshold = 0;
  bool upslope = false;
  unsigned int post_detect_block = initial_post_detect_block_();
  unsigned int post_detect_block_old = initial_post_detect_block_();
  bool crossing_detected = false;
  unsigned int nblock = 0;

  while (!context.terminated()) {
    if (!data_in_port_->slot(0)->RetrieveData(data_in_)) {
      break;
    }

    threshold = threshold_->get();
    upslope = upslope_->get();
    post_detect_block_old = post_detect_block;
    post_detect_block = post_detect_block_->get();

    if (post_detect_block_old != post_detect_block) {
      post_detection_block_update(post_detect_block);
    }

    // if blocking and post_detect_block value changed to a lower value, make
    // sure to update the current block value
    if (nblock > post_detect_block) {
      nblock = post_detect_block;
    }

    // loop through each sample
    for (unsigned int s = 0; s < data_in_->nsamples(); ++s) {
      if (nblock > 0) {
        --nblock;

        if (nblock == 0) {
          for (unsigned int c = 0; c < data_in_->nchannels(); ++c) {
            previous_sample_[c] = data_in_->data_sample(s, c);
          }
        }
        continue;
      }

      // loop through each channel
      for (unsigned int c = 0; c < data_in_->nchannels(); ++c) {
        // for up slope:
        if ((upslope && (previous_sample_[c] <= threshold) &&
             (data_in_->data_sample(s, c) > threshold)) ||
            (!upslope && (previous_sample_[c] >= threshold) &&
             (data_in_->data_sample(s, c) < threshold))) {
          crossing_detected = true;
          break;
        }
      }

      if (crossing_detected) {
        data_out_ = data_out_port_->slot(0)->ClaimData(false);
        data_out_->set_source_timestamp(data_in_->source_timestamp());
        data_out_->set_hardware_timestamp(data_in_->sample_timestamp(s));
        data_out_->set_serial_number(data_in_->serial_number());
        data_out_->set_event(event_prototype_());
        data_out_port_->slot(0)->PublishData();

        crossing_detected = false;
        ++n_detections_;

        nblock = post_detect_block;

        if ((n_detections_ % 50) == 0) {
          LOG(DEBUG) << name() << ". " << n_detections_
                     << " detections of event " << event_prototype_().event()
                     << " occurred.";
        }
      }

      for (unsigned int c = 0; c < data_in_->nchannels(); ++c) {
        previous_sample_[c] = data_in_->data_sample(s, c);
      }
    }

    data_in_port_->slot(0)->ReleaseData();
  }
}

void LevelCrossingDetector::Postprocess(ProcessingContext &context) {
  LOG(INFO) << name() << ". " << n_detections_ << " detections of event "
            << event_prototype_().event() << " occurred.";
  n_detections_ = 0;
}

void LevelCrossingDetector::post_detection_block_update(
    unsigned int post_detection_block) {
  double post_detection_block_us =
      post_detection_block /
      data_in_port_->streaminfo(0).parameters().sample_rate * 1e6;

  LOG(INFO) << name() << ". Post-detection block is set to "
            << post_detection_block_us << " microseconds.";

  if (post_detection_block_us < LOW_POST_DETECTION_BLOCK_US) {
    LOG(WARNING) << name() << ". Post-detection block might be too low!";
  }
}

REGISTERPROCESSOR(LevelCrossingDetector)