1 /*
2  *  spin_detector.cpp
3  *
4  *  This file is part of NEST.
5  *
6  *  Copyright (C) 2004 The NEST Initiative
7  *
8  *  NEST is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  NEST is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include "spin_detector.h"
24 
25 // C++ includes:
26 #include <numeric>
27 
28 // Includes from libnestutil:
29 #include "dict_util.h"
30 #include "compose.hpp"
31 #include "logging.h"
32 
33 // Includes from nestkernel:
34 #include "event_delivery_manager_impl.h"
35 #include "kernel_manager.h"
36 
37 // Includes from sli:
38 #include "arraydatum.h"
39 #include "dict.h"
40 #include "dictutils.h"
41 #include "doubledatum.h"
42 #include "integerdatum.h"
43 
spin_detector()44 nest::spin_detector::spin_detector()
45   : last_in_node_id_( 0 )
46   , t_last_in_spike_( Time::neg_inf() )
47 {
48 }
49 
spin_detector(const spin_detector & n)50 nest::spin_detector::spin_detector( const spin_detector& n )
51   : RecordingDevice( n )
52   , last_in_node_id_( 0 )
53   , t_last_in_spike_( Time::neg_inf() ) // mark as not initialized
54 {
55 }
56 
57 void
init_buffers_()58 nest::spin_detector::init_buffers_()
59 {
60 }
61 
62 void
calibrate()63 nest::spin_detector::calibrate()
64 {
65   RecordingDevice::calibrate( RecordingBackend::NO_DOUBLE_VALUE_NAMES, { nest::names::state } );
66 }
67 
68 void
update(Time const &,const long,const long)69 nest::spin_detector::update( Time const&, const long, const long )
70 {
71   if ( last_in_node_id_ != 0 ) // if last_* is empty we dont write
72   {
73     write( last_event_, RecordingBackend::NO_DOUBLE_VALUES, { static_cast< int >( last_event_.get_weight() ) } );
74     last_in_node_id_ = 0;
75   }
76 }
77 
78 nest::RecordingDevice::Type
get_type() const79 nest::spin_detector::get_type() const
80 {
81   return RecordingDevice::SPIN_DETECTOR;
82 }
83 
84 void
get_status(DictionaryDatum & d) const85 nest::spin_detector::get_status( DictionaryDatum& d ) const
86 {
87   // get the data from the device
88   RecordingDevice::get_status( d );
89 
90   if ( is_model_prototype() )
91   {
92     return; // no data to collect
93   }
94 
95   // if we are the device on thread 0, also get the data from the
96   // siblings on other threads
97   if ( get_thread() == 0 )
98   {
99     const std::vector< Node* > siblings = kernel().node_manager.get_thread_siblings( get_node_id() );
100     std::vector< Node* >::const_iterator s;
101     for ( s = siblings.begin() + 1; s != siblings.end(); ++s )
102     {
103       ( *s )->get_status( d );
104     }
105   }
106 }
107 
108 void
set_status(const DictionaryDatum & d)109 nest::spin_detector::set_status( const DictionaryDatum& d )
110 {
111   RecordingDevice::set_status( d );
112 }
113 
114 
115 void
handle(SpikeEvent & e)116 nest::spin_detector::handle( SpikeEvent& e )
117 {
118   // accept spikes only if detector was active when spike was
119   // emitted
120   if ( is_active( e.get_stamp() ) )
121   {
122     assert( e.get_multiplicity() > 0 );
123 
124     // The following logic implements the decoding
125     // A single spike signals a transition to the 0 state, two
126     // spikes at the same time step signal a transition to the 1
127     // state.
128     //
129     // Remember the node ID of the sender of the last spike being
130     // received this assumes that several spikes being sent by the
131     // same neuron in the same time step are received consecutively or
132     // are conveyed by setting the multiplicity accordingly.
133 
134     long m = e.get_multiplicity();
135     index node_id = e.get_sender_node_id();
136     const Time& t_spike = e.get_stamp();
137     if ( m == 1 && node_id == last_in_node_id_ && t_spike == t_last_in_spike_ )
138     {
139       // received twice the same node ID, so transition 0->1
140       // revise the last event
141       // if m == 2 this will just trigger writing in the following sections
142       last_event_.set_weight( 1.0 );
143     }
144     if ( last_in_node_id_ != 0 ) // if last_* is empty we dont write
145     {
146       // if it's the second event we write out the last event first
147       write( last_event_, RecordingBackend::NO_DOUBLE_VALUES, { static_cast< int >( last_event_.get_weight() ) } );
148     }
149     if ( m == 2 )
150     { // already full event
151       write( e, RecordingBackend::NO_DOUBLE_VALUES, { 1 } );
152       last_in_node_id_ = 0;
153     }
154     else
155     {
156       if ( last_in_node_id_ == 0 )
157       {
158         // store the new event as last_event_ for the next iteration
159         last_event_ = e;
160         last_event_.set_weight( 0.0 );
161         last_in_node_id_ = node_id;
162         t_last_in_spike_ = t_spike;
163       }
164       else
165       {
166         last_in_node_id_ = 0;
167       }
168     }
169   }
170 }
171