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