Digital signal means two things:
It can only be one of a limited number of values.
It only exists for a non-infinite amount of time.
Now that we have a sequence of numbers, our computer can do anything with it. It might, for example, apply digital filters, compress it, recognize speech, or transmit the signal using a digital link.
GNU Radio is mainly a framework for the development of signal processing blocks and their interaction.
GNU Radio is a framework to develop these processing blocks and create flowgraphs, which comprise radio processing applications.
That means that we can set properties using Python calls, such as calling a numpy or other GNU Radio functions. A common use of this is to call into the filter.firdes filter design tool from GNU Radio to build our filter taps.
“If a function x(t) contains no frequencies higher than B hertz, it is completely determined by giving its ordinates at a series of points spaced 1/(2B) seconds apart.”
Sample Rate Tutorial :
Sink Hardware Example
Source Hardware Example
Hardware Considerations
Setting the sample rate involves several factors to consider.
The various hardware devices have limits on what sample rates they can deliver. Some, such as the FunCube Pro+, have a fixed sample rate of 192kHz. Setting the flowgraph sample rate must be within the limitations of the device.
The computer hardware and operating system you are using will set limitations on the data throughput, such as:
USB2 vs USB3
processor speed
number of CPU cores
If you are using a USRP, data overruns are indicated by the letter ‘O’ displayed on the terminal screen. These are because the input data stream is producing data faster than the computer can consume it, so it could be due to a USB bottleneck, or the flowgraph is trying to do too much with those samples, or the CPU is not powerful enough, etc. Adjusting the sample rate and/or the input buffer size (where available) may alleviate the problem.
This flowgraph shows how an Embedded Python Block can be used to convert a string from a Message Edit Box to a byte string for the output port. The text is sent through a Throttle to a File Sink.
The file sink should have Unbuffered = On. In this particular case, the Throttle block is not required because all blocks will wait for a message string to be entered into the Message Edit Box.
In this way the Message Edit Box limits the number of samples produced. Text output is in the black screen at the bottom of the image.
–YAML GRC
-----Inputs and Outputs (optional)
This describes the input ports. domain can be either stream or message. Stream ports need a type, which usually is specified as a parameter. This is true for our example, the type is specified in type.t. The multiplicity tells us how many “copies” of this port we want. (Yes, this can be zero!) Finally, the optional flag tells us whether this port must be connected or not. (GRC won’t generate the flowgraph if a non-optional port isn’t connected)。
Tags, Messages and PMTs:
So far, we have only discussed data streaming from one block to another. The data often consists of samples, and a streaming architecture makes a lot of sense for those. For example, a sound card driver block will constantly produce audio samples once active.
In some cases, we don’t want to pipe a stream of samples, though, but rather pass individual messages to another block, such as “this is the first sample of a burst”, or “change the transmit frequency to 144 MHz”. Or consider a MAC layer on top of a PHY: At higher communication levels, data is usually passed around in PDUs (protocol data units) instead of streams of items.
Polymorphic Types are used as the carrier of data from one block/thread to another such as stream tags and message passing interfaces.
In GNU Radio we have two mechanisms to pass these messages:
Synchronously to a data stream, using stream tags
Asynchronously, using the message passing interface
Apart from the fixed position in the stream, stream tags have three properties:
A key, which can be used to identify a certain tag
A value, which can be any PMT
(Optional) A source ID, which helps identify the origin of this specific tag.
We now have a mechanism to randomly attach any metadata to specific items. There are several blocks that use tags. One of them is the UHD Sink block, the driver used for transmitting with USRP devices. It will react to tags with certain keys, one of them being tx_freq, which can be used to set the transmit frequency of a USRP while streaming.
Whether or not blocks use this information is up to them. Most blocks will propagate tags transparently, which means the tag is attached to the same item (or a corresponding item) on the output. Blocks that actually make use of tags usually search for tags with specific keys and only process those.
We now have a mechanism to randomly attach any metadata to specific items. There are several blocks that use tags. One of them is the UHD Sink block, the driver used for transmitting with USRP devices. It will react to tags with certain keys, one of them being tx_freq, which can be used to set the transmit frequency of a USRP while streaming.
tag propagation
We now know how to add tags to streams, and how to read them. But what happens to tags after they were read?
In the top half of the flow graph, we can see that it is, in fact, possible to switch between message passing and streaming ports, but only if the type of the PMTs matches the type of the streaming ports .
Another interesting fact is that we can connect more than one message output port to a single message input port, which is not possible with streaming ports.
What happens to a message once it was posted to a block? This depends on the actual block implementation, but there are two possibilities:
1) A message handler is called, which processes the message immediately.
2) The message is written to a FIFO buffer, and the block can make use of it whenever it likes, usually in the work function.
With a message passing interface, we can write blocks that don’t have streaming ports, and then the work function becomes useless, since it’s a function that is designed to work on streaming items. In fact, blocks that don’t have streaming ports usually don’t even have a work function.
==Note the msg_connect() call instead of the connect() function we use for streaming ports.==message ports
Simulation With GNURadio
GNU Radio is not primarily intended for simulations, but often these are an important step in the development of signal processing code. Using GNU Radio can even be advantageous at times, since the simulation code and the code to actually transmit over the air is always the same.
The C++ code (part 1)
The only interesting portion is the definition of the input and output signatures: At the input, we have 1 port that allows float inputs. The output port is the same.
void
square_ff_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required)
{
ninput_items_required[0] = noutput_items;
}
forecast() is a function which tells the scheduler how many input items are required to produce noutput_items output items. In this case, they’re the same. The index 0 indicates that this is for the first port, but we only have one any way. This is generally the case for forecast in a lot of blocks. For examples, you can look at how gr::block, gr::sync_block, gr::sync_decimator, and gr::sync_interpolator define the default forecast functions to account for things like rate changes and history.
Finally, there’s general_work(), which is pure virtual in gr::block, so we definitely need to override that. general_work() is the method that does the actual signal processing:
int
square_ff_impl::general_work (int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float *in = (const float *) input_items[0];
float *out = (float *) output_items[0];
for(int i = 0; i < noutput_items; i++) {
out[i] = in[i] * in[i];
}
// Tell runtime system how many input items we consumed on
// each input stream.
consume_each (noutput_items);
// Tell runtime system how many output items we produced.
return noutput_items;
}
There is one pointer to the input- and one pointer to the output buffer, respectively, and a for-loop which copies the square of the input buffer to the output buffer.
So, given history, vectors, multiple input ports etc., is this really all you need? Yes, it is!
If you use history of length k, GNU Radio will keep k-1 entries of the input buffer instead of discarding them. This means that if GNU Radio tells you the input buffer has N items, it actually has N+k-1 items you may use.
Block Coding Guide
Coding Structure
Implementation Header File
We normally define these files to use the same name as the public file and class with a ‘_impl’ suffix to indicate that this is the implementation file for the class.
Block Structure
3.2 IO Signatures
When creating a block, the user must communicate the following to the block:
The number of input ports.
The number of output ports.
The item size of each port.
A Work Function
Some observations:
Each buffer must be cast from a void* pointer into a usable data type.
The number of items in each input buffer is implied by noutput_items
More information on this in later sections
The number of items produced is returned, this can be less than noutput_items.
这里可以解释模块间的通信机制
Block Types
To take advantage of the gnuradio framework, users will create various blocks to implement the desired data processing. There are several types of blocks to choose from:
Synchronous Blocks (1:1)
Decimation Blocks (N:1)
Interpolation Blocks (1:M)
General Blocks (N:M)
Top Block
The top block is the main data structure of a GNU Radio flowgraph.
Stream Tags
A tag decorates a stream with metadata. A tag is associated with a particular item in a stream. An item may have more than one tag associated with it. The association of an item and tag is made through an absolute count. Every item in a stream has an absolute count. Tags use this count to identify which item in a stream to which they are associated.
Reading stream tags
Tips and Tricks
Saving State
Users should ensure that the state variables of the block are initialized property in the start() routine.
Polymorphic Types
Introduction
Polymorphic Types are opaque data types that are designed as generic containers of data that can be safely passed around between blocks and threads in GNU Radio.They are heavily used in the stream tags and message passing interfaces.
看完这一部分就掌握了GNURadio中数据类型的相关内容,数据的处理,数据类型的转化。PMTs是GNURadio内建的灵活的数据类型。
Handing Flowgraphs
Operating a Flowgraph
For each block, the number of items it can process is dependent on how much space it has in its output buffer(s) and how many items are available on the input buffer(s).
Latency and Throughput
这一部分讲了对输入、处理、输出数据的大小的限制。
以及对模块输入输出缓存区大小的设置方法
Metadata Information
注意区分sample rate 和 frequency
Introduction
Metadata files have extra information in the form of headers that carry metadata about the samples in the file.Raw, binary files carry no extra information and must be handled delicately. Any changes in the system state such as a receiver’s sample rate or frequency are not conveyed with the data in the file itself. Headers solve this problem.
Any changes in the system state such as a receiver’s sample rate or frequency are not conveyed with the data in the file itself. Headers solve this problem.
It contains information about the item size, data type, if it’s complex, the sample rate of the segment, the time stamp of the first sample of the segment, and information regarding the header size and segment size.
An optional extra section of the header stores information in any received tags. The two main tags associated with headers are:
rx_rate: the sample rate of the stream.
rx_time: the time stamp of the first item in the segment.
Types of Metadata Files
GNU Radio currently supports two types of metadata files:
- inline: headers are inline with the data in the same file.
- detached: headers are in a separate header file from the data.
Updating Headers
Structure
We finish this off by using pmt::serialize_str to convert the PMT dictionary into a specialized string format that makes it easy to write to a file.
We then want to get access to the item with key ‘strt’. As the next subsection will show, this value indicates at which byte the data segment starts.
Extras Information
pmt.intern() 很重要
Examples
这里面有讲怎样将一个时间戳放到流中,并将其serialize成pmt.str,(调用该数据时估计要deserialize,然后再访问字典。)
Message Passing
1. Introduction
GNU Radio was originally a streaming system with no other mechanism to pass data between blocks.
We solved part of this problem by introducing the tag stream (see Stream Tags). This is a parallel stream to the data streaming. The difference is that tags are designed to hold metadata and control information.
两种传送数据的方式:
data streaming
tag stream
GNU Radio’s message passing interface handles these cases, although it does so on an asynchronous basis.
The message passing interface heavily relies on Polymorphic Types (PMTs) in GNU Radio. For further information about these data structures, see the page Polymorphic Types (PMTs).
2. Message Passing API
gr::basic_block is the parent class for all blocks in GNU Radio.
GNURadio中有很多内建类 PMTs 适用于data streaming,为使模块功能适用于GNURadio架构,要处理好GNURadio内建类与python或C++中类的关系。
Tagged Stream Blocks
Introduction
These blocks are different from all the other GNU Radio block types (gr::block, gr::sync_block etc.) in that they are driven by the input: The PDU length tag tells the block how to operate, whereas other blocks are output-driven (the scheduler tries to fill up the output buffer as much as possible).
Creating a tagged stream block
The work() function looks very similar to any other work function. When writing the signal processing code, the following things must be kept in mind: - The work function is called for exactly one PDU, and no more (or less) may be processed - ninput_items contains the exact number of items in this PDU (at every port). These items will be consumed after work() exits. - Don’t call consume() or consume_each() yourself! gr::tagged_stream_block will do that for you. - You can call produce() or produce_each(), if you’re doing something complicated. Don’t forget to return WORK_CALLED_PRODUCE in that case.
Connecting regular streaming blocks and tagged stream blocks
From the scheduler’s point of view, all blocks are equivalent, and as long as the I/O signatures are compatible, all of these blocks can be connected.