Hi all,
        Following up on a thread from last year. After trying multiple 
approaches, I finally spent some time figuring out how to write a GnuRadio OOT 
module. My code is here <https://github.com/wmszeliga/gr-firehoseModule>.

In the OOT, I took the approach of having a separate thread using PCAP to 
listen to the hardware and populate a circular buffer. Then, the work() method 
of the OOT populates the output buffer using the circular buffer. While the 
code compiles, and the buffer doesn’t seem to get “lapped”, this approach is 
still yielding bad data. Using a separate program 
<https://github.com/wmszeliga/pcap_zmq> that just involves calling essentially 
the same PCAP routines and dumping the values straight to a file works yielding 
good data. The printing to STDERR may be slowing things down, but that was just 
a poor-mans debug; commenting those lines out doesn't change the resulting bad 
data. I’ve also tried various sizes for the circular buffer from 2^20 to 2^31, 
but larger sizes don’t seem to fix the issue either.

I understand that most folks don’t have the firehose hardware, but I was 
wondering if someone would skim my code and see if I was missing something 
obvious. I’m currently stuck with GnuRadio 3.8.5 because I’m using MacPorts. 
The biggest obstacle here is that 3.8.5 still uses SWIG rather than PyBind. 
That means I have hard-coded my device path (en5) rather than having it passed 
in through the block interface because I wasn’t in the mood to mess with SWIG 
any more than I had to.

Ultimately my plan is to use a ZMQ PUB Sink block, but for now, I’m testing 
using a File Sink block and then using a vendor-provided python script to plot 
the spectrum of the values in the file. Also, are my definitions of “good” and 
“bad” data: When I say that an approach yields “good data”, what I mean is that 
the spectrum of the data dumped to a file looks as it should. When I say that 
an approach yields “bad data”, the spectrum does not look as it should. To 
further confirm that the good spectrum actually has good data and the bad 
spectrum has bad data, there are a few other python scripts that I’ve run using 
the data that show high SNR for GPS/Galileo/GLONASS satellites that are 
actually visible when I capture good data but not when I capture bad data.

Does anything come to mind?

Thanks,
Walter

> On Jul 13, 2024, at 7:32 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