Program Listing for File spatialfeatures.cpp

Return to documentation for file (processors/spatialfeatures/spatialfeatures.cpp)

// ---------------------------------------------------------------------
// This file is part of falcon-core.
//
// Copyright (C) 2021-now 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 "spatialfeatures.hpp"
#include <algorithm>
#include <vector>


SpatialFeatures::SpatialFeatures() : IProcessor(){
    add_option("features", spatial_features_, "Selection of features to compute.", true);

}

void SpatialFeatures::CreatePorts(){

    spatial_in_port_ = create_input_port<ColumnsType<double>>("stimulus",
                                                              ColumnsType<double>::Capabilities(ChannelRange(1, std::numeric_limits<uint32_t>::max()),
                                                                                                SampleRange(0, std::numeric_limits<uint32_t>::max()), true),
                                                                PortInPolicy(SlotRange(1)));

    spike_in_port_ = create_input_port<ColumnsType<double>>("decoding features",
                                                            ColumnsType<double>::Capabilities(ChannelRange(1, std::numeric_limits<uint32_t>::max()),
                                                                                              SampleRange(0, std::numeric_limits<uint32_t>::max()), true),
                                                            PortInPolicy(SlotRange(0, MAX_NCHANNELS)));

    data_out_port_ = create_output_port<ColumnsType<double>>(ColumnsType<double>::Parameters(spatial_features_(), 0, true),
                                                             PortOutPolicy(SlotRange(0, MAX_NCHANNELS)));
}

void SpatialFeatures::CompleteStreamInfo() {

    nslots_ = spike_in_port_->number_of_slots();
    std::vector<std::string> features;
    if (spike_in_port_->number_of_slots() != data_out_port_->number_of_slots()) {
        auto err_msg = "Number of output slots (" +
                std::to_string(data_out_port_->number_of_slots()) +
                ") on port '" + data_out_port_->name() +
                "' does not match number of input slots (" +
                std::to_string(spike_in_port_->number_of_slots()) +
                ") on port '" + spike_in_port_->name() + "'.";
        throw ProcessingStreamInfoError(err_msg, name());
    }

    auto start = std::begin(spatial_in_port_->prototype(0).labels());
    auto stop = std::end(spatial_in_port_->prototype(0).labels());

    for(auto feat: spatial_features_()){
        if (std::find(start, stop, feat) == stop) {
            throw ProcessingStreamInfoError("Spatial feature " + feat +
                                            " does not exist in the data given in input of the spatial port. "
                                            "Available features are " + join(spatial_in_port_->prototype(0).labels()), name());
        }
    }

    for(slot_=0; slot_<nslots_; slot_++){
        features.clear();
        data_out_port_->streaminfo(slot_).set_stream_parameters(spike_in_port_->streaminfo(slot_));
        features = spike_in_port_->prototype(slot_).labels();
        std::copy(spatial_features_().begin(), spatial_features_().end(),  std::back_inserter(features));

        data_out_port_->streaminfo(slot_).set_parameters(ColumnsType<double>::Parameters(features, 0, true));
    }

}

void SpatialFeatures::Process(ProcessingContext &context){

    ColumnsType<double>::Data* spike_in = nullptr;
    ColumnsType<double>::Data* spatial_data= nullptr;
    ColumnsType<double>::Data* latest_spatial_data= nullptr;

    ColumnsType<double>::Data *data_out = nullptr;
    size_t sample;

    while (!context.terminated()) {
        if (!spatial_in_port_->slot(0)->RetrieveData(spatial_data, 0)) {
            break;

        }
        if(spatial_in_port_->slot(0)->status_read() == 1){
            latest_spatial_data = spatial_data;
            spatial_in_port_->slot(0)->ReleaseData();
        }


        for (slot_ = 0; slot_ < nslots_; ++slot_) {
            if (!spike_in_port_->slot(slot_)->RetrieveData(spike_in, 0)) {
                break;
            }

            if(spike_in_port_->slot(slot_)->status_read() == 0){
                continue;
            }

            if(latest_spatial_data == nullptr){
               spike_in_port_->slot(slot_)->ReleaseData();
               LOG(DEBUG) << "Cannot send spike features detected in waiting for spatial features.";
               continue;
            }

            data_out = data_out_port_->slot(slot_)->ClaimData(false);
            data_out->set_nsamples(spike_in->nsamples());

            for(sample=0; sample < spike_in->nsamples(); sample++ ){
                std::copy(spike_in->begin_sample(sample), spike_in->begin_sample(sample), data_out->begin_sample(sample));
                for(auto name: spatial_features_()){
                    data_out->set_data_sample(sample, name, latest_spatial_data->data_sample(0, name));
                }
            }

            data_out->set_source_timestamp();
            data_out->set_hardware_timestamp(spike_in->hardware_timestamp());
            // publish and release data

            data_out_port_->slot(slot_)->PublishData();
            spike_in_port_->slot(slot_)->ReleaseData();
        }
    }
}

REGISTERPROCESSOR(SpatialFeatures)