6个版本

0.0.7 2020年5月10日
0.0.6 2020年5月5日
0.0.5 2020年4月26日
0.0.3 2020年3月18日

#490 in 嵌入式开发

MIT许可证

83KB
1.5K SLoC

sac-base (信号与控制)

sac-base 是 sac-control 和 sac-signal 两个子crate的基础crate。这个crate仍在大力开发中,尚未完成。


想法

sac-* crate 帮助以网络为基础的方法实现信号处理和控制系统。类似于Simulink等示例。该项目主要针对嵌入式系统使用,因此是一个 [no_std] crate。与针对特定问题定制的系统实现相比,通用的网络方法永远不会那么快。但它将允许直接快速地在代码中实现和更改系统,而不是在Matlab中设计并导出到C。生成网络和构建拓扑相对昂贵。因此,crate在正常操作开始之前执行所有的分配和设置。这个基础crate的设计方式允许信号和控制crate轻松扩展它,并允许操作之间可以互换。sac-control 和 sac-signal 都在建设中,尚未提供。

基础crate

基础crate提供以下功能

  • 网络描述。
  • 基于基础节点的实现,操作基于此。
  • 简单的数学运算,如加法、减法。通常在信号和控制crate中都会使用到的操作。
  • 通用元素,如缓冲区。

no_std

库是用 no_std 构建的。因此,它可以在嵌入式系统上使用,虽然没有进行测试,但也可以在wasm上使用。

示例

以下示例可以通过以下代码构建

//
//    +----+
//    |    |
// +--+ 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_input(Buffer::new());
let in2 = network.add_input(Buffer::new());
let in3 = network.add_input(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();
// Register the multiplication as an output
network.as_output(&mul1);
// Insert a closed loop (Positive feedback loop)
network.close_loop(&mul1, &add2);
// Generate the network
network.build();
// Main loop operation
// Update the inputs
network.set_inputs(| index | {
    return 5.0;
});
// Iterate over the network
network.process();
let res = network.get_outputs();
// Update the inputs
network.set_inputs(| index | {
    return 10.0;
});
// Process twice to let the closed loop propagate
network.process();
let res = network.get_outputs();

扩展功能

很可能库没有实现您应用程序中需要的所有操作。实现一个新操作很简单,可以添加到库外部。

例如:假设您想添加一个新操作,该操作会丢弃每第三个元素。

// 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]);

展望

依赖项

约2MB
约36K SLoC