1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! # sac-base (Signal And Control)
//! sac-base is the base crate of its two child crates sac-control and sac-signal.
//! This crate is still under heavy construction and not finished.
//!
//! ---
//!
//! # Idea
//! The sac-* crates help to implement signal processing and control systems in a network based
//! approach. Similar to Simulink as example.
//! The project is designed for a primary usage on embedded systems and is therefore a [no_std]
//! crate. A generic solution such as the used network based approach will never be as fast as a
//! custom system implementation for a specific problem. But it will allow to directly and quickly
//! implement and change systems in code instead of designing it Matlab and exporting it to C.
//! Generation the network and building the topology is relatively expensive. Therefore the crate
//! does all the allocations and setup before the normal operation starts.
//! This base crate is designed in a way that allows the Signal and the Control crate to easily
//! extend it, and allow the operations to be interchangeable between each other.
//! Both the sac-control and the sac-signal are under construction and are not available yet.
//!
//! ## Base crate
//! The base crate provides the following features:
//! - Network description.
//! - Base node implementation which the operations are based on.
//! - Simple math operations such as adding, subtraction. Generally operations that will be used in both the Signal and Control crates.
//! - Generic elements such as buffers.
//!
//! ## no_std
//! The library is built with no_std. Therefore it can be used on embedded systems and while not tested, probably wasm aswell.
//!
//! # Example
//! The following example, can be build by the code given below:
//!
//! ```rust
//! //
//! //    +----+
//! //    |    |
//! // +--+ In +----+
//! //    |    |    |   +-----+
//! //    +----+    +---+     |
//! //                  | Add +----+-------------+
//! //    +----+    +---+     |    |             |
//! //    |    |    |   +-----+    |             |     +-----+
//! // +--+ In +----+              |             +-----+     |
//! //    |    |                   |                   | Mul +------+---+
//! //    +----+                   |             +-----+     |      |
//! //                             |  +-----+    |     +-----+      |
//! //    +----+                   +--+     |    |                  |
//! //    |    |                      | Add |    |                  |
//! // +--+ In +----------------------+     +----+                  |
//! //    |    |                      |     |                       |
//! //    +----+                +-----+     |                       |
//! //                          |     +-----+                       |
//! //                          |              +-----+              |
//! //                          |              |     |              |
//! //                          +--------------+ z^1 +--------------+
//! //                                         |     |
//! //                                         +---- +
//! //
//!
//! use sac_base::network::network::Network;
//! use sac_base::operations::math::add::Add;
//! use sac_base::operations::miscellaneous::buffer::Buffer;
//! use sac_base::operations::math::multiply::Multiply;
//! // Generate the network
//! let mut network = Network::new();
//! // Generate some operations
//! let add1 = network.add_operation(Add::new());
//! let mul1 = network.add_operation(Multiply::new());
//! let add2 = network.add_operation(Add::new());
//! // Add some inputs
//! let in1 = network.add_operation(Buffer::new());
//! let in2 = network.add_operation(Buffer::new());
//! let in3 = network.add_operation(Buffer::new());
//! // Connect the operations with each other
//! network.add_connection(&in1, &add1).unwrap();
//! network.add_connection(&in2, &add1).unwrap();
//! network.add_connection(&add1, &add2).unwrap();
//! network.add_connection(&in3, &add2).unwrap();
//! network.add_connection(&add1, &mul1).unwrap();
//! network.add_connection(&add2, &mul1).unwrap();
//! // Insert a closed loop (Positive feedback loop)
//! network.close_loop(&mul1, &add2);
//! // Generate the network
//! network.build().unwrap();
//! // Main loop operation
//! // Update the inputs
//! network.set_input_sample(&in1, 5.0);
//! network.set_input_sample(&in2, 5.0);
//! network.set_input_sample(&in3, 5.0);
//! // Iterate over the network
//! network.process();
//! let res = network.get_output(&mul1);
//! // Update the inputs
//! network.set_input_sample(&in1, 10.0);
//! network.set_input_sample(&in2, 10.0);
//! network.set_input_sample(&in3, 10.0);
//! // Process twice to let the closed loop propagate
//! network.process();
//! let res = network.get_output(&mul1);
//! ```
//!
//! # Extending the functionality
//! It is likely that the library does not implement all the operations that will be needed in your application.
//! Implementing a new operation is simple and can be added outside of the library.
//!
//! As example:
//! Lets say you want to add a new operation that drops every third element.
//! ```rust
//! // we need a possibility to count. So we need to add a count variable to the struct which will
//! // be boxed and moved into the node
//! use sac_base::network::node::Node;
//! use std::any::Any;
//! use sac_base::network::network::Network;
//! pub struct Third {
//!     count: usize,
//! }
//!
//! // Now define the functionality
//! impl Third {
//!
//!     pub fn new<T>() -> Node<T>
//!         where T: Copy + Default
//!     {
//!         // Generate the storage which is the defined struct in a boxed pointer which will be
//!         // moved into the node
//!         let storage = Box::new(Third{
//!             count: 0,
//!         }) as Box<dyn Any>;
//!         Node::new(storage, |data_input: (Vec<&[T]>, usize), data_buffer: &mut Box<dyn Any>, output: &mut Vec<T>| {
//!             // Get the storage back so we can access the count variable
//!             let storage: &mut Third = data_buffer.downcast_mut::<Third>().unwrap();
//!             let (inputs, max_len) = data_input;
//!             // Start iterating over the inputs. Here we only care about one input,
//!             // so we only take 1.
//!             inputs.into_iter().take(1).into_iter().for_each(|data| {
//!                 // Iterate over all the values of the input slice.
//!                 data.into_iter().take(max_len).into_iter().for_each(|v| {
//!                     // Modulo the count
//!                     storage.count = storage.count % 3;
//!
//!                     // Now we are ready to implement the functionality.
//!                     // The content of the vector will be passed into the next nodes as slice.
//!                     // We are only working with one sample, so we can just put it into the
//!                     // 0 position.
//!                     output.clear();
//!                     if storage.count == 2 {
//!                         // Write default in to the vector if its the third sample.
//!                         output.insert(0, T::default());
//!                     } else {
//!                         // Otherwise just use the normal value
//!                         output.insert(0, *v);
//!                     }
//!
//!                     // Increment the count and modulo it.
//!                     storage.count = storage.count + 1;
//!                 })
//!             })
//!         })
//!     }
//! }
//!
//! let mut network: Network<f32> = Network::new();
//!
//! // Insert the new node into the network
//! let third = network.add_operation(Third::new());
//!
//! network.build();
//!
//! network.set_input_slice(&third, &[1.0, 2.0, 3.0][..]);
//!
//! network.process();
//! let res = network.get_output(&third).unwrap();
//!
//! assert_eq!(res, [1.0, 2.0, 3.0]);
//! ```
//!
//! # Outlook
//! - Base crate:
//!     - Operations:
//!         - To chunk and from chuck operations
//!     - Far future:
//!         - Static operation
//! - Signal processing:
//!     - Checkout https://gitlab.com/signal-and-control/sac-signal
//! - Control:
//!     - Checkout https://gitlab.com/signal-and-control/sac-control

#![no_std]

#[cfg(test)]
#[macro_use]
extern crate std;

#[cfg(test)]
extern crate assert_approx_eq;

pub mod operations;
pub mod network;
pub mod helper;

extern crate alloc;
extern crate graphlib;
extern crate num;