From 071ecbd57ed29b61bc351ba4d72ae4a6c93db351 Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Sun, 5 Mar 2023 12:28:20 +0000 Subject: Initial working commit --- .gitignore | 2 + README.md | 15 ++++ airspyhf/__init__.py | 3 + airspyhf/airspyhf.py | 5 ++ airspyhf/libairspyhf.py | 200 ++++++++++++++++++++++++++++++++++++++++++++++++ test.py | 150 ++++++++++++++++++++++++++++++++++++ 6 files changed, 375 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 airspyhf/__init__.py create mode 100644 airspyhf/airspyhf.py create mode 100644 airspyhf/libairspyhf.py create mode 100644 test.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77f5cbe --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +airspyhf/__pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..9cd2359 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# README + +Python wrapper of airspyhf library + +https://github.com/airspy/airspyhf.git + +Project webpage main.lv + +## Sourcecode + +## Install + +## Links + +https://docs.python.org/3/library/ctypes.html \ No newline at end of file diff --git a/airspyhf/__init__.py b/airspyhf/__init__.py new file mode 100644 index 0000000..920681b --- /dev/null +++ b/airspyhf/__init__.py @@ -0,0 +1,3 @@ +from .libairspyhf import libairspyhf, airspyhf_lib_version_t, airspyhf_device_t_p, \ + airspyhf_sample_block_cb_fn, airspyhf_transfer_t_p, airspyhf_transfer_t +from .airspyhf import AirSpyHF \ No newline at end of file diff --git a/airspyhf/airspyhf.py b/airspyhf/airspyhf.py new file mode 100644 index 0000000..e13fada --- /dev/null +++ b/airspyhf/airspyhf.py @@ -0,0 +1,5 @@ +from .libairspyhf import libairspyhf + +class AirSpyHF: + def __init__(self): + pass \ No newline at end of file diff --git a/airspyhf/libairspyhf.py b/airspyhf/libairspyhf.py new file mode 100644 index 0000000..5da77ff --- /dev/null +++ b/airspyhf/libairspyhf.py @@ -0,0 +1,200 @@ +import sys +import os +from ctypes import * +from ctypes.util import find_library + + +def load_libairspyhf(): + if sys.platform == "linux" and 'LD_LIBRARY_PATH' in os.environ.keys(): + ld_library_paths = [local_path for local_path in os.environ['LD_LIBRARY_PATH'].split(':') if local_path.strip()] + ld_library_paths = ["/home/fam/prog/python/pyairspyhf/airspyhf/libairspyhf/src"] + driver_files = [local_path + '/libairspyhf.so' for local_path in ld_library_paths] + else: + driver_files = [] + driver_files += ['libairspyhf.so'] + #driver_files += ['airspyhf.dll', 'libairspyhf.so', 'libairspyhf.dylib'] + #driver_files += ['..//airspyhf.dll', '..//libairspyhf.so'] + driver_files += [lambda : find_library('airspyhf'), lambda : find_library('libairspyhf')] + dll = None + + for driver in driver_files: + if callable(driver): + driver = driver() + if driver is None: + continue + print(driver) + try: + dll = CDLL(driver) + break + except: + pass + else: + raise ImportError('Error loading libairspyhf. Make sure libairspyhf '\ + '(and all of its dependencies) are in your path') + + return dll + +libairspyhf = load_libairspyhf() + + +#typedef struct { +# uint32_t major_version; +# uint32_t minor_version; +# uint32_t revision; +#} airspyhf_lib_version_t; + +airspyhf_device_t_p = c_void_p +class airspyhf_lib_version_t(Structure): + _fields_ = [("major_version", c_uint32), + ("minor_version", c_uint32), + ("revision", c_uint32)] + +class airspyhf_complex_float_t(Structure): + _fields_ = [("re",c_float), + ("im",c_float)] +airspyhf_complex_float_t_p = POINTER(airspyhf_complex_float_t) + +class airspyhf_transfer_t(Structure): + _fields_ = [("device",airspyhf_device_t_p), + ("ctx",c_void_p), + ("samples",airspyhf_complex_float_t_p), + ("sample_count",c_int), + ("dropped_samples",c_uint64)] +airspyhf_transfer_t_p = POINTER(airspyhf_transfer_t) +#airspyhf_transfer_t_p = c_void_p + +#typedef int (*airspyhf_sample_block_cb_fn) (airspyhf_transfer_t* transfer_fn); +#airspyhf_sample_block_cb_fn = CFUNCTYPE(c_int, POINTER(airspyhf_transfer_t)) +airspyhf_sample_block_cb_fn = PYFUNCTYPE(c_int, POINTER(airspyhf_transfer_t)) +#void ADDCALL airspyhf_lib_version(airspyhf_lib_version_t* lib_version); +f = libairspyhf.airspyhf_lib_version +f.restype, f.argtypes = None, [POINTER(airspyhf_lib_version_t)] + +#int ADDCALL airspyhf_list_devices(uint64_t *serials, int count); +f = libairspyhf.airspyhf_list_devices +f.restype, f.argtypes = c_int, [POINTER(c_uint64), c_int] + +#int ADDCALL airspyhf_open(airspyhf_device_t** device); +f = libairspyhf.airspyhf_open +f.restype, f.argtypes = c_int, [POINTER(airspyhf_device_t_p)] + +#int ADDCALL airspyhf_open_sn(airspyhf_device_t** device, uint64_t serial_number); +f = libairspyhf.airspyhf_open_sn +f.restype, f.argtypes = c_int, [POINTER(airspyhf_device_t_p), c_uint64] + + +#int ADDCALL airspyhf_open_fd(airspyhf_device_t** device, int fd); +f = libairspyhf.airspyhf_open_fd +f.restype, f.argtypes = c_int, [POINTER(airspyhf_device_t_p), c_int] + +#int ADDCALL airspyhf_close(airspyhf_device_t* device); +f = libairspyhf.airspyhf_close +f.restype, f.argtypes = c_int, [airspyhf_device_t_p] + +#nt ADDCALL airspyhf_get_output_size(airspyhf_device_t* device); /* Returns the number of IQ samples to expect in the callback */ +f = libairspyhf.airspyhf_get_output_size +f.restype, f.argtypes = c_int, [airspyhf_device_t_p] + + +#int ADDCALL airspyhf_start(airspyhf_device_t* device, airspyhf_sample_block_cb_fn callback, void* ctx); +f = libairspyhf.airspyhf_start +f.restype, f.argtypes = c_int, [airspyhf_device_t_p,airspyhf_sample_block_cb_fn,py_object] + +#int ADDCALL airspyhf_stop(airspyhf_device_t* device); +f = libairspyhf.airspyhf_stop +f.restype, f.argtypes = c_int, [airspyhf_device_t_p] + +#int ADDCALL airspyhf_is_streaming(airspyhf_device_t* device); +f = libairspyhf.airspyhf_is_streaming +f.restype, f.argtypes = c_int, [airspyhf_device_t_p] + +#int ADDCALL airspyhf_is_low_if(airspyhf_device_t* device); /* Tells if the current sample rate is Zero-IF (0) or Low-IF (1) */ +f = libairspyhf.airspyhf_is_low_if +f.restype, f.argtypes = c_int, [airspyhf_device_t_p] + +#int ADDCALL airspyhf_set_freq(airspyhf_device_t* device, const uint32_t freq_hz); +f = libairspyhf.airspyhf_set_freq +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_uint32] + +#int ADDCALL airspyhf_set_freq_double(airspyhf_device_t* device, const double freq_hz); +f = libairspyhf.airspyhf_set_freq_double +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_double] + +#int ADDCALL airspyhf_set_lib_dsp(airspyhf_device_t* device, const uint8_t flag); /* Enables/Disables the IQ Correction, IF shift and Fine Tuning. */ +f = libairspyhf.airspyhf_set_lib_dsp +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_uint8] + +#int ADDCALL airspyhf_get_samplerates(airspyhf_device_t* device, uint32_t* buffer, const uint32_t len); +f = libairspyhf.airspyhf_get_samplerates +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, POINTER(c_uint32), c_uint32] + +#int ADDCALL airspyhf_set_samplerate(airspyhf_device_t* device, uint32_t samplerate); +f = libairspyhf.airspyhf_set_samplerate +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_uint32] + +#int ADDCALL airspyhf_get_calibration(airspyhf_device_t* device, int32_t* ppb); +f = libairspyhf.airspyhf_get_calibration +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, POINTER(c_int32)] + +#int ADDCALL airspyhf_set_calibration(airspyhf_device_t* device, int32_t ppb); +f = libairspyhf.airspyhf_set_calibration +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_int32] + +#int ADDCALL airspyhf_get_vctcxo_calibration(airspyhf_device_t* device, uint16_t* vc); +f = libairspyhf.airspyhf_get_vctcxo_calibration +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, POINTER(c_uint16)] + +#int ADDCALL airspyhf_set_vctcxo_calibration(airspyhf_device_t* device, uint16_t vc); +f = libairspyhf.airspyhf_set_vctcxo_calibration +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_uint16] + +#int ADDCALL airspyhf_set_optimal_iq_correction_point(airspyhf_device_t* device, float w); +f = libairspyhf.airspyhf_set_optimal_iq_correction_point +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_float] + +#int ADDCALL airspyhf_iq_balancer_configure(airspyhf_device_t* device, int buffers_to_skip, int fft_integration, int fft_overlap, int correlation_integration); +f = libairspyhf.airspyhf_iq_balancer_configure +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_int, c_int, c_int, c_int] + +#int ADDCALL airspyhf_flash_calibration(airspyhf_device_t* device); /* streaming needs to be stopped */ +f = libairspyhf.airspyhf_flash_calibration +f.restype, f.argtypes = c_int, [airspyhf_device_t_p] + +#int ADDCALL airspyhf_board_partid_serialno_read(airspyhf_device_t* device, airspyhf_read_partid_serialno_t* read_partid_serialno); +#f = libairspyhf.airspyhf_board_partid_serialno_read( +#f.restype, f.argtypes = c_int, [airspyhf_device_t_p] + +#int ADDCALL airspyhf_version_string_read(airspyhf_device_t* device, char* version, uint8_t length); +f = libairspyhf.airspyhf_version_string_read +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_char_p, c_uint8] + +#int ADDCALL airspyhf_set_user_output(airspyhf_device_t* device, airspyhf_user_output_t pin, airspyhf_user_output_state_t value); + + +#int ADDCALL airspyhf_set_hf_agc(airspyhf_device_t* device, uint8_t flag); /* 0 = off, 1 = on */ +f = libairspyhf.airspyhf_set_hf_agc +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_uint8] + +#int ADDCALL airspyhf_set_hf_agc_threshold(airspyhf_device_t* device, uint8_t flag); /* when agc on: 0 = low, 1 = high */ +f = libairspyhf.airspyhf_set_hf_agc_threshold +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_uint8] + +#int ADDCALL airspyhf_set_hf_att(airspyhf_device_t* device, uint8_t value); /* Possible values: 0..8 Range: 0..48 dB Attenuation with 6 dB steps */ +f = libairspyhf.airspyhf_set_hf_att +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_uint8] + +#int ADDCALL airspyhf_set_hf_lna(airspyhf_device_t* device, uint8_t flag); /* 0 or 1: 1 to activate LNA (alias PreAmp): 1 = +6 dB gain - compensated in digital */ +f = libairspyhf.airspyhf_set_hf_lna +f.restype, f.argtypes = c_int, [airspyhf_device_t_p, c_uint8] + +f = libairspyhf.py_test +f.restype, f.argtypes = None, [] + +f = libairspyhf.py_cb_wrapper +f.restype, f.argtypes = c_int, [airspyhf_device_t_p] + +f = libairspyhf.py_test_cb +f.restype, f.argtypes = c_int, [airspyhf_sample_block_cb_fn] + + +__all__ = ["libairspyhf", "airspyhf_lib_version_t", "airspyhf_device_t_p", "airspyhf_sample_block_cb_fn"] \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..8a70a14 --- /dev/null +++ b/test.py @@ -0,0 +1,150 @@ +import os +import airspyhf +from ctypes import * +import time +import sys +import wave +import struct + +print("Check airspyHF version") + +p = airspyhf.airspyhf_lib_version_t() +print(airspyhf.libairspyhf.airspyhf_lib_version(byref(p))) +print(p.major_version) +print(p.minor_version) +print(p.revision) + +print("Get list of devices if there is any") +ndev = airspyhf.libairspyhf.airspyhf_list_devices(None,0) +print("Found %d devices"%(ndev)) + +for devi in range(0,ndev): + serial = c_uint64(0) + airspyhf.libairspyhf.airspyhf_list_devices(byref(serial),devi+1) + print("Device %d: Serial number %s"%(int(devi),hex(serial.value) )) + +print("try to open device") +#device = POINTER(c_void_p) +#device_p = device() +dev_p = airspyhf.airspyhf_device_t_p(None) +ret = airspyhf.libairspyhf.airspyhf_open_sn(dev_p,0x3b52ab5dada12535) +print("open_sn: Returned %d"%(ret)) +if (ret != 0): + print("airspyhf_open_sn returned != 0, error") + sys.exit() + +print("List sample rates") +nsrates = c_uint32(0) + +ret = airspyhf.libairspyhf.airspyhf_get_samplerates(dev_p,byref(nsrates),c_uint32(0)) +print("ret %d"%ret) +print("sample rates %d"% nsrates.value) + +supportet_samplerates = (c_uint32*4)(0) +ret = airspyhf.libairspyhf.airspyhf_get_samplerates(dev_p,supportet_samplerates,nsrates) +print("ret %d"%ret) +for i in range(0,nsrates.value): + print("Sample rates %d"% supportet_samplerates[i]) + +#try to get some samples +ret = airspyhf.libairspyhf.airspyhf_set_samplerate(dev_p, supportet_samplerates[3]) +print(f"airspyhf_set_samplerate ret={ret}") + +ret = airspyhf.libairspyhf.airspyhf_set_hf_agc(dev_p, 1) +print(f"airspyhf_set_hf_agc ret={ret}") + +ret = airspyhf.libairspyhf.airspyhf_set_hf_agc_threshold(dev_p, 0) +print(f"airspyhf_set_hf_agc_threshold ret={ret}") + + +class CBthread: + def __init__(self,dev_p): + self.dev_p = dev_p + self.read_samples_cb = None + + def read_samples(self, transfer): + print("we here") + return 0 + + def start(self): + self.read_samples_cb = airspyhf.airspyhf_sample_block_cb_fn(self.read_samples) + ret = airspyhf.libairspyhf.airspyhf_start(self.dev_p, self.read_samples_cb, None) + print(f"airspyhf_start ret={ret}") + + def wait(self): + print("wait called") + +sample_count = 0 +wave_file = wave.open("record.wav","w") +wave_file.setnchannels(2) +wave_file.setsampwidth(4) +wave_file.setframerate(supportet_samplerates[1]) +#@CFUNCTYPE(c_int, airspyhf.airspyhf_transfer_t_p) +#@PYFUNCTYPE(c_int, airspyhf.airspyhf_transfer_t_p) +def read_samples(transfer): + global sample_count + global wave_file + #print("Python call back") + t = transfer.contents + bytes_to_write = t.sample_count * 4 * 2 + rx_buffer = t.samples + #print(f"{bytes_to_write} bytes receieved") + sample_count += t.sample_count + for i in range(0,t.sample_count): + d_re = t.samples[i].re + d_im = t.samples[i].im + data = struct.pack("