gnu radio学习多态类型PMT详解
Posted I_believe_CWJ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gnu radio学习多态类型PMT详解相关的知识,希望对你有一定的参考价值。
Polymorphic Types (PMTs)
Introduction(介绍)
Polymorphic Types are used as the carrier of data from one block/thread to another such as stream tags and message passing interfaces. PMT data types can represent a variety of data ranging from the Booleans to dictionaries. They are heavily used in the stream tags and message passing interfaces. In a sense, PMTs are a way to extend C++’ strict typing with something more flexible. The most complete list of PMT function is, of course, the source code, specifically the header file pmt.h. This page summarizes the most important features and points of PMTs.
多态数据类型通常用作从一个块/线程到另一个块/线程传输数据的载体,例如流标签和消息传递接口。PMT的数据类型可以表示各种各样的数据。它们被大量的使用在流标签和消息传递接口中。从某种意义上来说,PMT用一种更灵活的方法去扩展了c++严格类型。最完整的PMT函数列表当然是源代码,特别是头文件pmt.h
。本页总结了PMT最重要的特点以及要点。
Let’s dive straight into some Python code and see how we can use PMTs:
接下来让我们直接通过python代码来了解如何使用PMT。
>>> import pmt
>>> P = pmt.from_long(23)
>>> type(P)
<class 'pmt.pmt_swig.swig_int_ptr'>
>>> print P
23
>>> P2 = pmt.from_complex(1j)
>>> type(P2)
<class 'pmt.pmt_swig.swig_int_ptr'>
>>> print P2
0+1i
>>> pmt.is_complex(P2)
True
First, the pmt module is imported. We assign two values (P and P2) with PMTs using the from_long()
and from_complex()
calls, respectively. As we can see, they are both of the same type! This means we can pass these variables to C++ through SWIG, and C++ can handle this type accordingly.
首先,我们要导入pmt模块。我们使用form_long()
函数和from_complex()
函数来为PMTS(P 和 P2)来进行赋值。正如我们看到的,它们都是同样的类型!这意味着我们可以通过swig将这些变量传递给c++语言。同时c++语言可以相应的处理这些类型的变更了。
The same code as above in C++ would look like this:
同样的c++代码如下所示:
#include <pmt/pmt.h>
// [...]
pmt::pmt_t P = pmt::from_long(23);
std::cout << P << std::endl;
pmt::pmt_t P2 = pmt::from_complex(gr_complex(0, 1));
// Alternatively: pmt::from_complex(0, 1)
std::cout << P2 << std::endl;
std::cout << pmt::is_complex(P2) << std::endl;
Two things stand out in both Python and C++: First, we can simply print the contents of a PMT. How is this possible? Well, the PMTs have in-built capability to cast their value to a string (this is not possible with all types, though). Second, PMTs must obviously know their type, so we can query that, e.g. by calling the is_complex()
method.
PMT在python和c++的使用中有两点突出:第一点,我们都能简单的打印一个pmt类型的内容。为什么可以这样操作呢?这是因为PMT又有一个可以将它们的值转换为字符串的内置功能(虽然这对于其他所有类型来说是不可能的)第二,PMT在使用的过程中必须清楚的知道它们自己的类型,所以我们可以进行查询,例如通过is_complex()
方法。
Note: When running the above as a standalone, the compiler command will look something like g++ pmt_tutorial.cpp -I$(gnuradio-config-info --prefix)/include -lgnuradio-pmt -o pmt_tutorial
注意:当我们独立运行上述的代码时,编译器的指令看起来像:g++ pmt_tutorial.cpp -I$(gnuradio-config-info --prefix)/include -lgnuradio-pmt -o pmt_tutorial
When assigning a non-PMT value to a PMT, we can use the from_*
methods, and use the to_*
methods to convert back:
当我们将一个非PMT类型的值转化为PMT类型时我们可以使用from_*
方法,同时相反的我们可以使用to_*
方法将PMT类型的值转化为非PMT类型。
pmt::pmt_t P_int = pmt::from_long(42);
int i = pmt::to_long(P_int);
pmt::pmt_t P_double = pmt::from_double(0.2);
double d = pmt::to_double(P_double);
pmt::pmt_t P_double = pmt::mp(0.2);
The last row shows the pmt::mp()
shorthand function. It basically saves some typing, as it infers the correct from_ function
from the given type.
代码的最后一行展示了pmt::mp()
速记函数的使用。它基本的节省了一些输入,因为它可以通过给出的变量类型推测出正确的from_*()
函数。
String types play a bit of a special role in PMTs, as we will see later, and have their own converter:
字符串类型在PMT类型中扮演了一个非常重要的角色,在稍后我们会看到,并且它们有自己的转换器。
pmt::pmt_t P_str = pmt::string_to_symbol("spam");
pmt::pmt_t P_str2 = pmt::intern("spam");
std::string str = pmt::symbol_to_string(P_str);
The pmt::intern
is another way of saying pmt::string_to_symbol
.
pmt::intern()
是实现pmt::string_to_symbol()
函数的另一种方法。
See the PMT docs and the header file pmt.h
for a full list of conversion functions.
有关转换函数的完整列表可以查看PMT的文档以及头文件pmt.h
In Python, we can make use of the dynamic typing, and there’s actually a helper function to do these conversions (C++ also has a helper function for converting to PMTs called pmt::mp()
, but it’s less powerful, and not quite as useful, because types are always strictly known in C++):
在python语言中我们可以使用动态数据类型,同时那里事实上还有一个辅助函数来实现类型转换(c++同样也有一个辅助函数用于PMT类型的转换叫做pmt::mp()
,但是它的功能不是那么的强大,也不是特别的实用,因为在c++中数据类型一帮都是严格已知的):
P_int = pmt.to_pmt(42)
i = pmt.to_python(P_int)
P_double = pmt.to_pmt(0.2)
d = pmt.to_double(P_double)
On a side note, there are three useful PMT constants, which can be used in both Python and C++ domains. In C++, these can be used as such:
附带说明一下,这里有三个非常有用的PMT常量,它们都可以在python和c++域中使用。例如在c++中是这样使用的:
pmt::pmt_t P_true = pmt::PMT_T;
pmt::pmt_t P_false = pmt::PMT_F;
pmt::pmt_t P_nil = pmt::PMT_NIL;
在python中是这样使用的:
pmt::pmt_t P_true = pmt::PMT_T;
pmt::pmt_t P_false = pmt::PMT_F;
pmt::pmt_t P_nil = pmt::PMT_NIL;
pmt.PMT_T
and pmt.PMT_F
are boolean PMT types representing True
and False
, respectively. The PMT_NIL
is like a NULL
or None and can be used for default arguments or return values, often indicating an error has occurred.
pmt.PMT_T
和pmt.PMT_F
是布尔类型的变量分别表示true和false。PMT_NIL就类似于NULL或者None类型的变量并且常用于默认参数和返回值,通常表示发生了某个错误。
To be able to go back to C++ data types, we need to be able to find out the type from a PMT. The family of is_*
methods helps us do that:
为了能够回到c++数据类型,我们需要找出PMT的数据类型,一系列的is_*()
方法可以帮我们实现。
double d;
if (pmt::is_integer(P)) {
d = (double) pmt::to_long(P);
} else if (pmt::is_real(P)) {
d = pmt::to_double(P);
} else {
// We really expected an integer or a double here, so we don't know what to do
throw std::runtime_error("expected an integer!");
}
It is important to do type checking since we cannot unpack a PMT of the wrong data type.
进行数据类型的检查是非常重要的,因为我们不能解压一个错误数据类型的PMT。
We can compare PMTs without knowing their type by using the pmt::equal()
function:
我们能可以利用pmt::equal()
函数在不知道PMT数据类型的情况下进行比较。
if (pmt::eq(P_int, P_double)) {
std::cout << "Equal!" << std::endl; // This line will never be reached
There are more equality functions, which compare different things: pmt::eq()
and pmt::eqv()
. We won’t need these for this tutorial.
这里有更多的相同作用的函数用来比较不同的数据类型,例如pmt::eq()
以及pmt::eqv()
函数。本次教程不需要这些
The rest of this page provides more depth into how to handle different data types with the PMT library.
本页剩余的部分将更深入的介绍如何使用PMT库处理不同的数据类型。
More Complex Types(更复杂的数据类型)
PMTs can hold a variety of types. Using the Python method pmt.to_pmt()
, we can convert most of Pythons standard types out-of-the-box:
PMT能容纳多种的数据类型。利用python方法中的pmt.to_pmt()
函数,我们能够转换大多数的python标准类型开箱即用。
P_tuple = pmt.to_pmt((1, 2, 3, 'spam', 'eggs'))
P_dict = pmt.to_pmt({'spam': 42, 'eggs': 23})
But what does this mean in the C++ domain? Well, there are PMT types that define tuples and dictionaries, keys and values being PMTs, again.
但是这在c++中意味着什么呢?好的,这里有PMT类型定义的元组和字典,键值和值也都是PMT类型。
So, to create the tuple from the Python example, the C++ code would look like this:
所以,要像python那样创建元组,c++的代码如下所示:
pmt::pmt_t P_tuple = pmt::make_tuple(pmt::from_long(1), pmt::from_long(2), pmt::from_long(3), pmt::string_to_symbol("spam"), pmt::string_to_symbol("eggs"))
For the dictionary, it’s a bit more complex:
对于数据字典,它会稍微有点复杂:
pmt::pmt_t P_dict = pmt::make_dict();
P_dict = pmt::dict_add(P_dict, pmt::string_to_symbol("spam"), pmt::from_long(42));
P_dict = pmt::dict_add(P_dict, pmt::string_to_symbol("eggs"), pmt::from_long(23));
As you can see, we first need to create a dictionary, then assign every key/value pair individually.
正如你所看到的,我们首先需要创建一个数据字典,然后各自为它们赋值键值对的值。
A variant of tuples are vectors. Like Python’s tuples and lists, PMT vectors are mutable, whereas PMT tuples are not. In fact, PMT vectors are the only PMT data types that are mutable. When changing the value or adding an item to a dictionary, we are actually creating a new PMT.
元组的一个变体是向量。就像python中的元组和列表,PMT向量是可变的,但是PMT元组是不可变的。事实上,PMT向量是PMT数据类型中唯一可变的类型。当我们改变字典中的值或者向字典中添加元素时,我们实际上创造了一个新的PMT。
To create a vector, we can initialize it to a certain lengths, and fill all elements with an initial value. We can then change items or reference them:
创建一个向量,我们可以初始化给它一个确定的长度,并且用一个值来初始化所有的元素。然后我们可以改变元素或者引用它们。
pmt::pmt_t P_vector = pmt::make_vector(5, pmt::from_long(23)); // Creates a vector with 5 23's as PMTs
pmt::vector_set(P_vector, 0, pmt::from_long(42)); // Change the first element to a 42
std::cout << pmt::vector_ref(P_vector, 0); // Will print 42
In Python, we can do all these steps (using pmt.make_vector()
etc.), or convert a list:
在python中,我们可以执行所有的步骤(使用pmt.make_vector()
方法等等),或者转换一个列表:
P_vector = pmt.to_pmt([42, 23, 23, 23, 23])
Vectors are also different from tuples in a sense that we can directly load data types into the elements, which don’t have to be PMTs.
从某种意义来讲向量不同于元组,例如我们可以直接向元组中加入不是PMT数据类型的元素。
Say we want to pass a series of 8 float values to another block (these might be characteristics of a filter, for example). It would be cumbersome to convert every single element to and from PMTs, since all elements of the vector are the same type.
假设我们想传输大量的8位浮点数的值到另一个块(例如,这可能是一个过滤器的特点)。将每一个元素都与PMT类型进行转换会变得很麻烦,因为向量中的所有元素都是同一个类型。
We can use special vector types for this case:
对于这种情况,我们可以使用特殊的向量类型:
pmt::pmt_t P_f32vector = pmt::make_f32vector(8, 5.0); // Creates a vector with 8 5.0s's as floats
pmt::f32vector_set(P_f32vector, 0, 2.0); // Change the first element to a 2.0
float f = f32vector_ref(P_f32vector, 0);
std::cout << f << std::endl; // Prints 2.0
size_t len;
float *fp = pmt::f32vector_elements(P_f32vector, len);
for (size_t i = 0; i < len; i++)
std::cout << fp[i] << std::endl; // Prints all elements from P_f32vector, one after another
Python has a similar concept: Numpy arrays. As usual, the PMT library understands this and converts as expected:
python中也有类似的概念:Numpy arrays。像往常一样,PMT库明白这一点并按预期进行转换:
P_f32vector = pmt.to_pmt(numpy.array([2.0, 5.0, 5.0, 5.0, 5.0], dtype=numpy.float32))
print pmt.is_f32vector(P_f32vector) # Prints 'True'
Here, ‘f32’ stands for ‘float, 32 bits’. PMTs know about most typical fixed-width data types, such as ‘u8’ (unsigned 8-bit character) or ‘c32’ (complex with 32-bit floats for each I and Q). Consult the manual for a full list of types.
这里,‘f32’代表32位的浮点数。PMT知道大多数典型固定宽度的数据类型,例如‘u8’(8位无符号字符)或者‘c32’(对于每个I和Q都具有32位浮点数)。有关类型的详细列表,请看参考手册。
The most generic PMT type is probably the blob (binary large object). Use this with care - it allows us to pass around anything that can be represented in memory.
最通用的PMT类型大概是blob类型(binary large object二进制大对象)。注意小心使用:它可以传递任何可以在内存中表示的数据。
PMT Data Type(PMT数据类型)
All PMTs are of the type pmt::pmt_t. This is an opaque container and PMT functions must be used to manipulate and even do things like compare PMTs. PMTs are also immutable (except PMT vectors). We never change the data in a PMT; instead, we create a new PMT with the new data. The main reason for this is thread safety. We can pass PMTs as tags and messages between blocks and each receives its own copy that we can read from. However, we can never write to this object, and so if multiple blocks have a reference to the same PMT, there is no possibility of thread-safety issues of one reading the PMT data while another is writing the data. If a block is trying to write new data to a PMT, it actually creates a new PMT to put the data into. Thus we allow easy access to data in the PMT format without worrying about mutex locking and unlocking while manipulating them.
所有的PMT都用pmt::pmt_t来表示。这是一个不透明的容器并且必须使用PMT类型函数来进行操作和比较。PMT同样是不可变的(除了PMT向量)。我们从来补改变PMT中的数据;相反的我们根据新的数据创建新的PMT。主要的原因是我们要保证线程安全。我们可以像标签和信息一样在块与块之间传递PMT数据,并且每个接收块我们可以从接收器中读取接收的信息。但是我们永远无法写入此对象,因此,如果多个块引用同一个PMT,由于线程安全,我们不可能从同时在一个对一个PMT读取和写入数据。如果一个块尝试写入新的数据到PMT,事实上它创建了一个新的PMT来写入数据我们可以轻松的访问PMT中的数据而不用在操作时担心互斥锁和解锁问题。
PMTs can represent the following:
PMT可以表示如下的数据类型
Boolean values of true/false
Strings (as symbols)
Integers (long and uint64)
Floats (as doubles)
Complex (as two doubles)
Pairs
Tuples
Vectors (of PMTs)
Uniform vectors (of any standard data type)
Dictionaries (list of key:value pairs)
Any (contains a boost::any pointer to hold anything)
The PMT library also defines a set of functions that operate directly on PMTs such as:
PMT同时还定义了一系列可以直接操作PMT的函数,如下:
Equal/equivalence between PMTs
Length (of a tuple or vector)
Map (apply a function to all elements in the PMT)
Reverse
Get a PMT at a
position in a list
Serialize and deserialize
Printing
The constants in the PMT library are:
PMT库中的常量如下:
pmt::PMT_T - a PMT True
pmt::PMT_F - a PMT False
pmt::PMT_NIL - an empty PMT (think Python’s ‘None’)
Inserting and Extracting Data(插入和提取数据)
Use pmt.h for a complete guide to the list of functions used to create PMTs and get the data from a PMT. When using these functions, remember that while PMTs are opaque and designed to hold any data, the data underneath is still a C++ typed object, and so the right type of set/get function must be used for the data type.
通过使用头文件pmt.h来得到用于创建PMT的函数列表以及从pmt得到数据。当我们使用这些函数的时候要注意,尽管PMT是不透明的并且用于储存各种数据,但是下面的数据类型依旧是c++对象,所以对于不同的数据类型必须选用合适类型的set/get函数。
Typically, a PMT object can be made from a scalar item using a call like pmt::from_<type>
. Similarly, when getting data out of a PMT, we use a call like “pmt::to_”. For example:
通常,可以使用一个一个类似形如pmt::from_<type>
的方法来创建PMT对象。类似的,当我们从PMT对象中获取数据时,我们可以使用一个形如pmt::to_<type>
的方法来实现。例如:
double a = 1.2345;
pmt::pmt_t pmt_a = pmt::from_double(a);
double b = pmt::to_double(pmt_a);
int c = 12345;
pmt::pmt_t pmt_c = pmt::from_long(c);
int d = pmt::to_long(pmt_c);
As a side-note, making a PMT from a complex number is not obvious:
需要注意的是,从一个使用复数来创建PMT是不明显的。
std::complex<double> a(1.2, 3.4);
pmt::pmt_t pmt_a = pmt::make_rectangular(a.real(), a.imag());
std::complex<double> b = pmt::to_complex(pmt_a);
Pairs, dictionaries, and vectors have different constructors and ways to manipulate them, and these are explained in their own sections.
对,字典以及向量都有着不同的构造函数和方法去使用它们,这些都在它们自己的库中有着解释。
Strings(字符串)
PMTs have a way of representing short strings. These strings are actually stored as interned symbols in a hash table, so in other words, only one PMT object for a given string exists. If creating a new symbol from a string, if that string already exists in the hash table, the constructor will return a reference to the existing PMT.
PMT有一种方法来表示短的字符串。这些字符串实际上作为内部符号储存在哈希表中,所以换句话说对于一个字符串只有一个包含该字符串的PMT对象存在。如果为一个字符串创建一个对象,如果发现该字符串已经存在在哈希表中了,构造函数会返回一个指向存在
We create strings with the following functions, where the second function, pmt::intern
, is simply an alias of the first.
我们通过下面的函数来创建字符串,这里的第二个函数pmt::intern
只是第一个函数的别名。
pmt::pmt_t str0 = pmt::string_to_symbol(std::string("some string"));
pmt::pmt_t str1 = pmt::intern(std::string("some string"));
The string can be retrieved using the inverse function:
我们同样可以使用相反类型的函数来得到字符串。
std::string s = pmt::symbol_to_string(str0);
Tests and Comparisons(测试和比较)
The PMT library comes with a number of functions to test and compare PMT objects. In general, for any PMT data type, there is an equivalent pmt::is_<type>
. We can use these to test the PMT before trying to access the data inside. Expanding our examples above, we have:
PMT库包含了大量的函数来测试和比较PMT对象。一般来说,对于任意的PMT数据类型,都有一个与之对应的函数pmt::_is<type>
。我们可以在我们获取PMT里面存放的数据之前来检测数据类型。扩展上面的例子我们可以得到:
pmt::pmt_t str0 = pmt::string_to_symbol(std::string("some string"));
if(pmt::is_symbol(str0))
std::string s = pmt::symbol_to_string(str0);
double a = 1.2345;
pmt::pmt_t pmt_a = pmt::from_double(a);
if(pmt::is_double(pmt_a))
double b = pmt::to_double(pmt_a);
int c = 12345;
pmt::pmt_t pmt_c = pmt::from_long(c);
if(pmt::is_long(pmt_a))
int d = pmt::to_long(pmt_c);
\\\\ This will fail the test. Otherwise, trying to coerce \\b pmt_c as a
\\\\ double when internally it is a long will result in an exception.
if(pmt::is_double(pmt_a))
double d = pmt::to_double(pmt_c);
Dictionaries(数据字典)
PMT dictionaries are lists of key:value pairs. They have a well-defined interface for creating, adding, removing, and accessing items in the dictionary. Note that every operation that changes the dictionary both takes a PMT dictionary as an argument and returns a PMT dictionary. The dictionary used as an input is not changed and the returned dictionary is a new PMT with the changes made there.
PMT字典是键值对的列表。它们都有一个定义好的接口用来创建,增加,删除以及获取字典中的项目。注意,每个改变数据字典的操作都需要用PMT作为参数并且返回值也是一个PMT数据字典。被用作输入的数据字典并没有改变,同时返回的数据字典也是一个新的修改的PMT对象。
The following is a list of PMT dictionary functions. View each function in the GNU Radio C++ Manual to get more information on what each does.
下面列出了一系列的字典函数。查看GNU Radio C++手册中的每个函数可以得到它们的功能的更详细介绍。
bool pmt::is_dict(const pmt_t &obj)
pmt_t pmt::make_dict()
pmt_t pmt::dict_add(const pmt_t &dict, const pmt_t &key, const pmt_t &value)
pmt_t pmt::dict_delete(const pmt_t &dict, const pmt_t &key)
bool pmt::dict_has_key(const pmt_t &dict, const pmt_t &key)
pmt_t pmt::dict_ref(const pmt_t &dict, const pmt_t &key, const pmt_t ¬_found)
pmt_t pmt::dict_items(pmt_t dict)
pmt_t pmt::dict_keys(pmt_t dict)
pmt_t pmt::dict_values(pmt_t dict)
This example does some basic manipulations of PMT dictionaries in Python. Notice that we pass the dictionary a and return the results to a. This still creates a new dictionary and removes the local reference to the old dictionary. This just keeps our number of variables small.
此示例在python语言中对PMT字典做了一些基本的操作。注意我们传递了字典a同时返回了结果到字典a。这同样创建了一个新的字典同时对旧字典的本地引用。这只是保持我们的变量数比较小。
import pmt
key0 = pmt.intern("int")
val0 = pmt.from_long(123)
val1 = pmt.from_long(234)
key1 = pmt.intern("double")
val2 = pmt.from_double(5.4321)
# Make an empty dictionary
a = pmt.make_dict()
# Add a key:value pair to the dictionary
a = pmt.dict_add(a, key0, val0)
print a
# Add a new value to the same key;
# new dict will still have one item with new value
a = pmt.dict_add(a, key0, val1)
print a
# Add a new key:value pair
a = pmt.dict_add(a, key1, val2)
print a
# Test if we have a key, then delete it
print pmt.dict_has_key(a, key1)
a = pmt.dict_delete(a, key1)
print pmt.dict_has_key(a, key1)
ref = pmt.dict_ref(a, key0, pmt.PMT_NIL)
print ref
# The following should never print
if(pmt.dict_has_key(a, key0) and pmt.eq(ref, pmt.PMT_NIL)):
print "Trouble! We have key0, but it returned PMT_NIL"
Vectors(向量类型)
PMT vectors come in two forms: vectors of PMTs and vectors of uniform data. The standard PMT vector is a vector of PMTs, and each PMT can be of any internal type. On the other hand, uniform PMTs are of a specific data type which come in the form:
PMT向量有两种形式组成:PMT向量和统一数据类型向量。标准PMT向量是一个由多个PMT对象组成的向量,每个PMT可以是任何内部类型。另一方面,统一类型的PMT具有特定的数据类型,主要有一下几种类型:
(u)int8
(u)int16
(u)int32
(u)int64
float32
float64
complex 32 (std::complex<float>)
complex 64 (std::complex<double>)
That is, the standard sizes of integers, floats, and complex types of both signed and unsigned.
也就是说,标准大小的整数、浮点数以及复数类型都包括有符号和无符号的。
Vectors have a well-defined interface that allows us to make, set, get, and fill them. We can also get the length of a vector with pmt::length, or in Python:
向量组有一个定义好的接口允许我们去创建、设置、获取和填充它们。在c++中我们同样可以通过方法pmt::length来得到向量组的长度,或者在python中像下面一样得到:
pmt_t p1 = pmt_integer(1);
pmt_t p2 = pmt_integer(2);
pmt_t p3 = pmt_integer(3);
pmt_t p_list = pmt_list3(p1, p2, p3);
pmt_length(p_list); // Returns 3
For standard vectors, these functions look like:
对于标准向量组,它们的函数如下所示:
以上是关于gnu radio学习多态类型PMT详解的主要内容,如果未能解决你的问题,请参考以下文章
[SDR] GNU Radio 系列教程(十四) —— GNU Radio 低阶到高阶用法的分水岭 ZMQ 的使用详解