On 13/07/2024 12:55, Ian Buckley wrote:
As an aside…. I wonder if the original USRP2 transport code is still captured 
in the public domain. This used a proprietary Ethernet transport for IQ before 
we switched to UDP in UHD transport.
I think that was part of GnuRadio at the time, so pulling a very old Gnu Radio might yield the original pre-UHD code for
  the USRP2...


Sent from my iPhone

On Jul 13, 2024, at 7:33 AM, Marcus Müller <mmuel...@gnuradio.org> wrote:

Hi Walter,

interesting project!

The libpcap approach seems to be reasonable; backintheday, I used to capture at 
Ethernet frame level using socket(PF_PACKET,…), but that's pretty non-portable 
and comes with its own can of worms. pcap's the way to go there, I'd say, 
unless you want add a protocol family to your operating system (don't know 
whether that is Linux or Mac OS), which I kind of doubt.

However:
The PUB/SUB scheme is almost certainly not what you want here – that is for 
broadcasting data to multiple subscribers (or dropping them, when the 
subscriber(s) aren't ready) from potentially multiple transmitters. You might 
spot the problem here! If you attach a waterhose on one end, and the other end 
doesn't fetch packets in intervals short enough for the receive buffer to not 
overflow, these packets will just silently be dropped - business as usual for a 
PUBlisher! Try the PUSH/PULL pattern: GNU Radio by principle will need a block 
like the SUB block to fetch data as available, and call it back later at some 
point. That will not work well in this use case.

So, to your core question, writing a GNU Radio block for your device is 
relatively easy, probably; data rates aren't *that* high, so an extra memory 
copy here and there is something I'd live with for a prototype.
Methodology would be this, roughly:
1. make an out-of-tree module. We cover this on https://tutorials.gnuradio.org 
, specifically in [1]. In short, `gr_modtool newmod yourmodname`.
2. Make a source block (`gr_modtool add -t source -l c++ hose_source`)
3. in the generated lib/something_impl.cc, add a `bool hose_source::start() 
{}`, and also add tht `bool start() override;` method prototype to the _impl.h
4. in the _impl.h add private fields: a set of buffers, one for each channel, where 
you'll put the data "deinterleavedly" from the NIC. Make each buffer some (say, 
2²⁰) GNSS samples long. Also add two unsigned integer counters: one read and one write 
index. And because we're lazy and don't care *that* much about performance yet, two 
mutexes (one for securing access to the read index, and one for the write index).
5. in the constructor, you allocate these buffers, set the read index to the 
length of the buffers (-1) and the write index to 0
6. in the start() method, you spawn a thread that, in a loop
  1. checks how much space there is between read and write index (get read 
mutex, fetch read index value, release mutex, calculate difference)
  2. uses pcap to fetch packets, (but only as much as the space calculated 
above allows for!), deinterleaves data onto the buffers, finally
  4. updates write index (get write mutex, update write index, release write 
mutex)
7. the block's work() method is called by GNU Radio regularly and
  1. checks how much data is between write and read indices (get write mutex, 
read write index, release mutex, calculate difference)
  2. checks whether that's more or less than the space for output items 
available in this current call to work(), takes the minimum of both
  3. gets that amount of items from each buffer and writes them to the output 
buffer, as passed as argument to the work() method (you could do type 
conversions here!)
  4. updates the read index accordingly (get read mutex, update index, release 
mutex)
  5. returns the number of written items

note that the index updating and distance calculation need to take the 
"wraparound" at the end of the buffer into account.

Also note: very similarly, you could write a **SoapySDR** driver instead of a 
GNU Radio block. You could then use the Generic SoapySDR Source block to get 
data from that driver, and other, non-GNU Radio programs, could be using the 
driver just as well, without knowing about the hardware. I don't think the 
basic principle would be much different: you need an IO thread that keeps the 
NIC busy, and because readers might be slow, an internal buffer, which you 
ideally use constructively (instead of just incurring a memory bandwidth 
overhead), to deinterleave channels on ingress, and to convert data types on 
egress, if you will.

Note that one *could* potentially, as mentioned above write a zero-copy-ish driver for 
GNU Radio 3.10+ using our custom buffer framework and something like AF_XDP, dpdk, or 
io_uring, but I think that would very much a) leave the scope of what anyone be able to 
assist you with – to the best of my knowledge, you'd be the first to do that with a 
network device – and b) we're "only" talking gigabit ethernet here, and you got 
a fast machine. As you said in your email, in principle, things are plenty fast enough, 
so let's not overcomplicate.

[1] 
https://wiki.gnuradio.org/index.php?title=Creating_C%2B%2B_OOT_with_gr-modtool

On 12.07.24 22:42, Walter Szeliga wrote:
Hi all,
     I have a GNSS Firehose 
(https://transitiva.com/product/gnss-firehose-receiver-tri-band-quad-constellation/ 
<https://transitiva.com/product/gnss-firehose-receiver-tri-band-quad-constellation/>)
 and have been trying to get it working, in a streaming capacity, with Gnuradio. The 
Firehose sends packets over ethernet using the experimental ethertype 0x88b5. I've 
tried a few things to get data from the Firehose into Gnuradio, some have worked, 
others have not. Things that work:
* Use tcpdump and filter on 0x88b5 and save to a file. Open and repackage each 
packet in the pcap dump as interleaved bytes of I&Q and save to a file. Read 
into Gnuradio.
* Write a custom program using libpcap to filter on 0x88b5 on a selected 
interface, repackage the packets and write directly to a file. Read into 
Gnuradio.
* Write a custom program using libpcap to filter on 0x88b5 on a selected 
interface, repackage the packets and PUB them using ZMQ. SUB to this PUB using 
a simple Python script and dump the message contents to a file. Read into 
Gnuradio. Both tcp and ipc PUB/SUB work equally well.
Some things that do not work:
* SUB to the ZMQ PUB/SUB using the Gnuradio ZMQ SUB Source block.
* Write a custom program using libpcap to filter on 0x88b5 on a selected 
interface, repackage the packets and send them using UDP.
The UDP approach doesn't work because too many packets get dropped and I have 
been unable to set sysctl values appropriately (on an M1 Mac) to avoid dropping 
too many packets.
I'm not sure why the ZMQ approach does not work with Gnuradio. I've tried many 
simple flowgraphs to convert from vector to stream, ibyte to complex, you name 
it, and then dumping the data back out to a file using a File Sink (just to use 
existing software to check for data sanity). Data gets into Gnuradio, but it 
clearly loses something because constellation diagrams of the output data 
become blobs centered on the origin rather than pairs of point clouds offset 
from the origin as one would expect from the BPSK nature of the GNSS signals 
captured by the Firehose.
I've come to the realization that it's probably best to write some sort of 
driver to get data straight from the Firehose into Gnuradio, but I have no idea 
where to start with this. Any ideas about how to fix my ZMQ approach or start 
writing a custom driver would be appreciated. There's a lot in here, so let me 
know if you would like code or flowgraph examples.
Cheers,
Walter


Reply via email to