SoapySDR icon indicating copy to clipboard operation
SoapySDR copied to clipboard

C#/.Net/Mono bindings

Open guruofquality opened this issue 8 years ago • 10 comments

We are interested in doing other language bindings for SoapySDR so that anyone can get involved in SDR using their favourite language, tools, and libraries. We may already have a volunteer to tackle C# bindings for SoapySDR, but first we need to know if there is any interest. Please let us know in the comments. Thanks!

guruofquality avatar Nov 04 '15 04:11 guruofquality

:+1: Would also be interested in a UWP Component for Windows 10.

ooeygui avatar Feb 09 '16 03:02 ooeygui

Would be interested in using C# mono bindings in Xamarin.

cjam avatar Jan 24 '17 16:01 cjam

Yes I'm interested in C# interface to SoapySDR (and in turn to SDRPlay RSP2). I'd be happy to help but I've never done anything like this before. If someone would help me get started. I was able to build SoapySDR dlls using Windows 10 VS2015.

Ronkpa avatar Apr 25 '17 18:04 Ronkpa

@guruofquality , do you know if a c# has been created for soapy? Or a java one (which should be close)? I've searched but found nothing for soapy itself.

I saw there was a semi-old project of java binding for Pothos also, but I'd be more interested in the soapy layer.

OhSoGood avatar Mar 30 '18 10:03 OhSoGood

Not that I know of. Given that SoapySDR bindings for python are basically swig based, I think we could get away with another swig .i file and some build glue for C# or java (which I do think are similar). It would be a lot simpler than the way Pothos handles bindings (because he calls into the language from c++ runtime). Any volunteers? :-)

guruofquality avatar Mar 30 '18 17:03 guruofquality

@guruofquality , I've tried to create something, but I'm afraid that's very very alpha, as I'm 100% new to swig. Would you mind helping me on that?

Here is my SoapySDR.i, copied and adapted from the python one: `// Copyright (c) 2014-2017 Josh Blum // Copyright (c) 2016-2016 Bastille Networks // SPDX-License-Identifier: BSL-1.0

%module SoapySDR

//////////////////////////////////////////////////////////////////////// // Include all major headers to compile against //////////////////////////////////////////////////////////////////////// %{ #include <SoapySDR/Version.hpp> #include <SoapySDR/Modules.hpp> #include <SoapySDR/Device.hpp> #include <SoapySDR/Errors.hpp> #include <SoapySDR/Formats.hpp> #include <SoapySDR/Time.hpp> #include <SoapySDR/Logger.hpp> %}

//////////////////////////////////////////////////////////////////////// // http://www.swig.org/Doc2.0/Library.html#Library_stl_exceptions //////////////////////////////////////////////////////////////////////// %include <exception.i>

%exception { try{$action} catch (const std::exception &ex) {SWIG_exception(SWIG_RuntimeError, ex.what());} catch (...) {SWIG_exception(SWIG_RuntimeError, "unknown");} }

//////////////////////////////////////////////////////////////////////// // Config header defines API export //////////////////////////////////////////////////////////////////////// %include <SoapySDR/Config.h>

//////////////////////////////////////////////////////////////////////// // Commonly used data types //////////////////////////////////////////////////////////////////////// // %include <std_complex.i> %include <std_string.i> %include <std_vector.i> %include <std_map.i> %include <SoapySDR/Types.hpp>

//handle arm 32-bit case where size_t and unsigned are the same #ifdef SIZE_T_IS_UNSIGNED_INT %typedef unsigned int size_t; #else %template(SoapySDRUnsignedList) std::vector; #endif

%template(SoapySDRKwargs) std::map<std::string, std::string>; %template(SoapySDRKwargsList) std::vectorSoapySDR::Kwargs; %template(SoapySDRArgInfoList) std::vectorSoapySDR::ArgInfo; %template(SoapySDRStringList) std::vectorstd::string; %template(SoapySDRRangeList) std::vectorSoapySDR::Range; %template(SoapySDRSizeList) std::vector<size_t>; %template(SoapySDRDoubleList) std::vector;

// %extend std::map<std::string, std::string> // { // %insert("proxycode") // %{ // def str(self): // Change into ToString() // out = list() // for k, v in self.iteritems(): // out.append("%s=%s"%(k, v)) // return '{'+(', '.join(out))+'}' // %} // };

// %extend SoapySDR::Range // { // %insert("proxycode") // %{ // def str(self): // Change into ToString() // fields = [self.minimum(), self.maximum()] // if self.step() != 0.0: fields.append(self.step()) // return ', '.join(['%g'%f for f in fields]) // %} // };

//////////////////////////////////////////////////////////////////////// // Stream result class // Helps us deal with stream calls that return by reference //////////////////////////////////////////////////////////////////////// %inline %{ struct StreamResult { StreamResult(void): ret(0), flags(0), timeNs(0), chanMask(0){} int ret; int flags; long long timeNs; size_t chanMask; }; %}

// %extend StreamResult // { // %insert("proxycode") // %{ // def str(self) // Change into ToString() // return "ret=%s, flags=%s, timeNs=%s"%(self.ret, self.flags, self.timeNs) // %} // };

//////////////////////////////////////////////////////////////////////// // Constants SOAPY_SDR_* //////////////////////////////////////////////////////////////////////// %include <SoapySDR/Constants.h> %include <SoapySDR/Errors.h> %include <SoapySDR/Version.h> %include <SoapySDR/Formats.h>

%ignore SoapySDR_logf; %ignore SoapySDR_vlogf; %ignore SoapySDR_registerLogHandler; %include <SoapySDR/Logger.h>

//////////////////////////////////////////////////////////////////////// // Utility functions //////////////////////////////////////////////////////////////////////// %include <SoapySDR/Errors.hpp> %include <SoapySDR/Version.hpp> %include <SoapySDR/Modules.hpp> %include <SoapySDR/Formats.hpp> %include <SoapySDR/Time.hpp>

%ignore SoapySDR::logf; %ignore SoapySDR::vlogf; %ignore SoapySDR::registerLogHandler; %include <SoapySDR/Logger.hpp>

//////////////////////////////////////////////////////////////////////// // Device object //////////////////////////////////////////////////////////////////////// // %nodefaultctor SoapySDR::Device; %include <SoapySDR/Device.hpp>

// //global factory lock support // %proxycode %{

// all = list() // for key in sorted(globals().keys()): // if key.startswith('SOAPY_SDR_'): // all.append(key) // %}

// //make device a constructable class // %insert("proxycode") // %{ // _Device = Device // class Device(Device): // Device Create(*args, **kwargs) // { // return Device.make(*args, **kwargs) // }

// def extractBuffPointer(buff): // if hasattr(buff, 'array_interface'): return buff.array_interface['data'][0] // if hasattr(buff, 'buffer_info'): return buff.buffer_info()[0] // if hasattr(buff, 'long'): return long(buff) // if hasattr(buff, 'int'): return int(buff) // raise Exception("Unrecognized data format: " + str(type(buff))) // %}

%extend SoapySDR::Device { StreamResult readStream__(SoapySDR::Stream *stream, const std::vector<size_t> &buffs, const size_t numElems, const int flags, const long timeoutUs) { StreamResult sr; sr.flags = flags; std::vector<void *> ptrs(buffs.size()); for (size_t i = 0; i < buffs.size(); i++) ptrs[i] = (void *)buffs[i]; sr.ret = self->readStream(stream, (&ptrs[0]), numElems, sr.flags, sr.timeNs, timeoutUs); return sr; }

StreamResult writeStream__(SoapySDR::Stream *stream, const std::vector<size_t> &buffs, const size_t numElems, const int flags, const long long timeNs, const long timeoutUs)
{
    StreamResult sr;
    sr.flags = flags;
    std::vector<const void *> ptrs(buffs.size());
    for (size_t i = 0; i < buffs.size(); i++) ptrs[i] = (const void *)buffs[i];
    sr.ret = self->writeStream(stream, (&ptrs[0]), numElems, sr.flags, timeNs, timeoutUs);
    return sr;
}

StreamResult readStreamStatus__(SoapySDR::Stream *stream, const long timeoutUs)
{
    StreamResult sr;
    sr.ret = self->readStreamStatus(stream, sr.chanMask, sr.flags, sr.timeNs, timeoutUs);
    return sr;
}

// %insert("proxycode")
// %{
    // #call unmake from custom deleter
    // def __del__(self):
        // Device.unmake(self)

    // def __str__(self):
        // return "%s:%s"%(self.getDriverKey(), self.getHardwareKey())

    // def readStream(self, stream, buffs, numElems, flags = 0, timeoutUs = 100000):
        // ptrs = [extractBuffPointer(b) for b in buffs]
        // return self.readStream__(stream, ptrs, numElems, flags, timeoutUs)

    // def writeStream(self, stream, buffs, numElems, flags = 0, timeNs = 0, timeoutUs = 100000):
        // ptrs = [extractBuffPointer(b) for b in buffs]
        // return self.writeStream__(stream, ptrs, numElems, flags, timeNs, timeoutUs)

    // def readStreamStatus(self, stream, timeoutUs = 100000):
        // return self.readStreamStatus__(stream, timeoutUs)
// %}

}; `

OhSoGood avatar Apr 03 '18 20:04 OhSoGood

Yes, I’m interested in calling the LimeSDR Mini from C#. Target platform will be .NET 6 on Raspberry Pi 4. Did the SWIG file above ever get tested? Did any bindings ever get generated? Did the API change much since the file above was written?

M0LTE avatar Dec 28 '21 08:12 M0LTE

Hi @M0LTE , our needs changed in the meantime and we went coding directly in C, so I didn't go further on the C# binding. What I had done looked promising, but I never tested it. As the API seldom changes (almost never I'd say), I encourage you to build up on it and to share with others.

OhSoGood avatar Dec 28 '21 13:12 OhSoGood

https://github.com/pothosware/SoapySDR/tree/wip/csharp

I have .NET bindings in the works, but due to CMake (and sanity) limitations, it's only buildable with MSVC.

It's mostly done, and I don't anticipate too much C#-specific API churn, but no guarantees on that.

ncorgan avatar Dec 28 '21 16:12 ncorgan

ncorgan - Thanks for the timely update - I am working on a few projects now with Pluto and SDRPlay devices and it would be good for me to have the same 'hook' to them via Soapy. Right now I am using their respective DLL's. I (we) appreciate your work on this. I use C# because its: Fast, easy, powerful, and reliable. Plus it's supported and cost effective.

Hagtronics avatar Dec 29 '21 15:12 Hagtronics

Closing, support was recently merged in master.

ncorgan avatar Nov 24 '22 02:11 ncorgan