diff options
author | Arturs Artamonovs <dos21h@gmail.com> | 2023-03-09 22:47:36 +0000 |
---|---|---|
committer | Arturs Artamonovs <dos21h@gmail.com> | 2023-03-09 22:47:36 +0000 |
commit | 194f0c07ecbab61eb383c9d17c72fb73a4c9af80 (patch) | |
tree | a70fdd9a8e1235e1b35fbdd735ecaf9cdb7c0e41 | |
parent | c1ae0bcdb4eb27404d132b108f3d8da5ccdcf817 (diff) | |
parent | fba22599d2ce386517c5c7f93403e0b3a4c92877 (diff) | |
download | pyairspyhf-194f0c07ecbab61eb383c9d17c72fb73a4c9af80.tar.gz pyairspyhf-194f0c07ecbab61eb383c9d17c72fb73a4c9af80.zip |
Adding test for class. Implementeing main class. cleaning/fixing airspyhf_rx
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | README.md | 39 | ||||
-rw-r--r-- | airspy_waterfall.py | 153 | ||||
-rw-r--r-- | airspyhf/airspyhf.py | 78 | ||||
-rw-r--r-- | airspyhf/libairspyhf.py | 2 | ||||
-rwxr-xr-x | airspyhf_rx.py | 48 | ||||
-rw-r--r-- | airspyhf_shedule.py | 8 | ||||
-rwxr-xr-x[-rw-r--r--] | test.py | 5 | ||||
-rwxr-xr-x | test_class.py | 13 |
9 files changed, 322 insertions, 25 deletions
@@ -1,2 +1,3 @@ .idea airspyhf/__pycache__/ +*.wav
\ No newline at end of file @@ -5,8 +5,9 @@ Python wrapper of airspyhf library https://github.com/airspy/airspyhf.git Project webpage main.lv +main project page http://main.lv +cgit source viewer http://git.main.lv/cgit.cgi/pyairspyhf.git -http://git.main.lv/cgit.cgi/pyairspyhf.git # Source code @@ -14,6 +15,35 @@ http://git.main.lv/cgit.cgi/pyairspyhf.git git clone https://git.main.lv/cgit/pyairspyhf.git ``` +# Install airspyhf + +Install all dependencies such as: +``` + cmake, libusb +``` + +Checked supported and tested version list + +To build the libairspyhf follow: https://github.com/airspy/airspyhf + +Workes localy +``` +git clone https://github.com/airspy/airspyhf.git +cd airspyhf +cmake . +make +sudo make install +``` + +If there is issues with libusb header try to find libusb header locations +``` +cmake -DLIBUSB_INCLUDE_DIR=/usr/include/libusb-1.0/ -DINSTALL_UDEV_RULES=ON +``` + +```commandline +udevadm control --reload-rules +``` + # API ## libairspyhf @@ -23,6 +53,13 @@ git clone https://git.main.lv/cgit/pyairspyhf.git ## airspyhf_rx.py +## Supported and tested + +| --- | --- | --- | --- | +| Python | libairspyhf | OS | Status | +| --- |--- | --- | --- | +| 3.9, 3.10 | 1.7.1 | ArchLinux, Ubuntu 20.04 | Supported and tested | + ## Install diff --git a/airspy_waterfall.py b/airspy_waterfall.py new file mode 100644 index 0000000..255ecd5 --- /dev/null +++ b/airspy_waterfall.py @@ -0,0 +1,153 @@ +import os +import sys +import math + +import airspyhf + +import matplotlib +import numpy +import pylab + +import pygame +from pygame import gfxdraw + +CENTER_FREQ = 4e6 +SAMPLE_RATE = 196e3 +SAMPLE_NUM = 2048 +GAIN = 'auto' + +SCREEN_X = 1025 +SCREEN_Y = 320 + +MOVE_STEP = 0.1e6 + +# init RTLSDR and if no then go out +try: + pass +except IOError: + print + "Probably RTLSDR device not attached" + sys.exit(0) + +# config rtlsdr device +#rtl.sample_rate = SAMPLE_RATE +#rtl.center_freq = CENTER_FREQ +#rtl.gain = GAIN + + +def iq_abs(c): + return (math.sqrt((c.real ** 2 + c.imag ** 2))) + + +# point should be normalised to 0.0 ... 1.0 +def color_normalise(point): + ret = (255, 0, 0) + # blue + if (point < 0.3): + ret = (0, 0, int(point * 255 * 3.3)) + # yello + elif (point < 0.7): + ret = (0, int((point - 0.3) * 255 * 2.5), 0) + # red + elif (point <= 1.0): + ret = (int((point - 0.7) * 255 * 3.3), 0, 0) + else: + # print "Color Error ", point + pass + return ret + + +def color_mapping(x): + "assumes -50 to 0 range, returns color" + r = int((x + 50) * 255 // 50) + r = max(0, r) + r = min(255, r) + return (r, r, 100) + + +# def draw_Hz( surface, x, y, hz ): + + +arr = [[0 for i in range(0, SCREEN_X)] for j in range(0, SCREEN_Y)] + +# init all pygame modules audio,video and more +pygame.init() + +# [NEW] creates screen surface using constants +screen = pygame.display.set_mode((SCREEN_X, SCREEN_Y)) + +#samples = rtl.read_samples(SAMPLE_NUM) +samples = [] + +run = True +line = 0 +while run: + + # print "update" + + # check for all events that where ocure + for event in pygame.event.get(): + # if some one clicked on close button + if event.type == pygame.QUIT: + # terminate programm + run = False + # don't waste your time by waiting while event loop will end + break + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_LEFT: + print + "Left" + rtl.center_freq -= MOVE_STEP + print + "Center freq: ", rtl.center_freq, " Hz" + elif event.key == pygame.K_RIGHT: + print + "Right" + rtl.center_freq += MOVE_STEP + print + "Center freq: ", rtl.center_freq, " Hz" + + width = SCREEN_X + height = SCREEN_Y + + samples = rtl.read_samples(SAMPLE_NUM) + spect = numpy.fft.fft(samples) + spect = spect[0:len(spect) / 2] + + # (1/(Fs*N)) * abs(xdft).^2; + spect_n = [(1.0 / (SAMPLE_NUM * len(spect))) * iq_abs(x) ** 2 for x in spect] + spect_n = (10 * numpy.log10(spect_n)).tolist() + + # total data size + spect_len = len(spect_n) + + # calculate amount spect points per pixel without rounding + pixel_width = spect_len / SCREEN_X + 1 + pixel_steps = spect_len / pixel_width + + for step in range(0, pixel_steps): + avg = 0.0 + for i in range(0, pixel_width): + avg += spect_n[step * pixel_width + i] + avg /= pixel_width + + # print avg + # gfxdraw.pixel( screen, step, line, color_normalise((100-abs(avg))/10)) + gfxdraw.pixel(screen, step, line, color_mapping(avg)) + + # draw central freq + font = pygame.font.Font(None, 20) + text = font.render(str(rtl.center_freq / 1e6), 1, (200, 30, 30), (0, 0, 0)) + screen.blit(text, (SCREEN_X / 2, SCREEN_Y - 20)) + text = font.render(str((rtl.center_freq + SAMPLE_RATE / 2) / 1e6), 1, (200, 30, 30), (0, 0, 0)) + screen.blit(text, (SCREEN_X - 40, SCREEN_Y - 20)) + text = font.render(str((rtl.center_freq - SAMPLE_RATE / 2) / 1e6), 1, (200, 30, 30), (0, 0, 0)) + screen.blit(text, (20, SCREEN_Y - 20)) + + pygame.display.flip() + line += 1 + if (line > SCREEN_Y): + line = 0 + +pygame.quit + diff --git a/airspyhf/airspyhf.py b/airspyhf/airspyhf.py index e13fada..4314c76 100644 --- a/airspyhf/airspyhf.py +++ b/airspyhf/airspyhf.py @@ -1,5 +1,77 @@ -from .libairspyhf import libairspyhf +from .libairspyhf import * +from ctypes import * class AirSpyHF: - def __init__(self): - pass
\ No newline at end of file + dev_p = airspyhf_device_t_p(None) + sample_rates = [] + def __init__(self,): + self.dev_p = None + + def open(self, device_index:int=None, serialnumber:int=None): + if serialnumber is not None: + ret = libairspyhf.airspyhf_open_sn(self.dev_p, serialnumber) + if ret != 0: + print("Cant open device by serial number") + return -1 + elif device_index is not None: + ndev = libairspyhf.airspyhf_list_devices(None, 0) + if ndev < device_index+1: + print("Device index higher then device num") + return -1 + serial = c_uint64(0) + libairspyhf.airspyhf_list_devices(byref(serial), device_index + 1) + return 0 + + def get_samplerates(self): + nsrates = c_uint32(0) + ret = libairspyhf.airspyhf_get_samplerates(self.dev_p, byref(nsrates), c_uint32(0)) + if ret != 0: + print("Cant get number of avaliable sample rates") + return [] + supported_samplerates = (c_uint32 * nsrates)(0) + ret = libairspyhf.airspyhf_get_samplerates(self.dev_p, supported_samplerates, nsrates) + if ret != 0: + print("Cant get avaliable sample rate list") + return [] + self.sample_rates = list(supported_samplerates) + return self.sample_rates + + def set_samplerate(self, samplerate:int): + if self.sample_rates == []: + self.get_samplerates() + if samplerate not in self.sample_rates: + print(f"Unknown sample rate. Avaliable samplerate {self.sample_rates}") + return -1 + + ret = libairspyhf.airspyhf_set_samplerate(self.dev_p, samplerate) + if ret != 0: + print("Cannot set samplerate") + return -1 + + return 0 + + def set_hf_agc(self,flag): + ret = libairspyhf.airspyhf_set_hf_agc(self.dev_p, flag) + return ret + + def set_hf_agc_threshold(self,flag): + ret = libairspyhf.airspyhf_set_hf_agc_threshold(self.dev_p, flag) + return ret + + def set_hf_att(self, value): + ret = libairspyhf.airspyhf_set_hf_att(dev_p, value) + return ret + + def set_hf_lna(self,flag): + ret = libairspyhf.airspyhf_set_hf_lna(dev_p, 1) + return ret + + def close(self): + ret = libairspyhf.close(self.dev_p) + if ret != 0: + print("Cant close device") + + + + + diff --git a/airspyhf/libairspyhf.py b/airspyhf/libairspyhf.py index dada9f5..a287626 100644 --- a/airspyhf/libairspyhf.py +++ b/airspyhf/libairspyhf.py @@ -23,7 +23,7 @@ def load_libairspyhf(): driver = driver() if driver is None: continue - print(driver) + #print("Search for driver named %s"%(driver)) try: dll = CDLL(driver) break diff --git a/airspyhf_rx.py b/airspyhf_rx.py index ac84856..a3b5634 100755 --- a/airspyhf_rx.py +++ b/airspyhf_rx.py @@ -1,3 +1,4 @@ +#!/usr/bin/python3 import os from airspyhf import * from ctypes import * @@ -8,10 +9,10 @@ import struct import argparse parser = argparse.ArgumentParser() -parser.add_argument("--frequency") -parser.add_argument("--samplerate") -parser.add_argument("--outputfile") -parser.add_argument("--serial") +parser.add_argument("-f","--frequency") +parser.add_argument("-s","--samplerate") +parser.add_argument("-o","--outputfile") +parser.add_argument("-sn","--serial") args = parser.parse_args() print("Check airspyHF version") @@ -38,7 +39,9 @@ if args.serial != None: else: serial = c_uint64(0) libairspyhf.airspyhf_list_devices(byref(serial), 1) - ret = libairspyhf.airspyhf_open_sn(dev_p, f"{hex(serial.value)}") + print(hex(serial.value)) + print(type(serial.value)) + ret = libairspyhf.airspyhf_open_sn(dev_p, serial.value) print("open_sn: Returned %d"%(ret)) if (ret != 0): print("airspyhf_open_sn returned != 0, error") @@ -51,14 +54,21 @@ ret = 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 = libairspyhf.airspyhf_get_samplerates(dev_p,supportet_samplerates,nsrates) +supported_samplerates = (c_uint32*4)(0) +ret = libairspyhf.airspyhf_get_samplerates(dev_p,supported_samplerates,nsrates) print("ret %d"%ret) +print("Sample rate list:") for i in range(0,nsrates.value): - print("Sample rates %d"% supportet_samplerates[i]) + print(" %d s/sec"% supported_samplerates[i]) #try to get some samples -ret = libairspyhf.airspyhf_set_samplerate(dev_p, supportet_samplerates[3]) +supported_samplerates = list(supported_samplerates) +if int(args.samplerate) in supported_samplerates: + print("Setting sample rate to %s"%(args.samplerate)) + ret = libairspyhf.airspyhf_set_samplerate(dev_p, int(args.samplerate)) +else: + print("Setting sample rate to %s"% (supported_samplerates[len(supported_samplerates)-1])) + ret = libairspyhf.airspyhf_set_samplerate(dev_p, supported_samplerates[len(supported_samplerates)-1]) print(f"airspyhf_set_samplerate ret={ret}") ret = libairspyhf.airspyhf_set_hf_agc(dev_p, 1) @@ -67,11 +77,17 @@ print(f"airspyhf_set_hf_agc ret={ret}") ret = libairspyhf.airspyhf_set_hf_agc_threshold(dev_p, 0) print(f"airspyhf_set_hf_agc_threshold ret={ret}") +ret = libairspyhf.airspyhf_set_hf_lna(dev_p, 1) +print(f"airspyhf_set_hf_lna ret={ret}") + sample_count = 0 -wave_file = wave.open("record.wav","w") +RECORD_FILE_NAME="record.wav" +if args.outputfile != None: + RECORD_FILE_NAME = args.outputfile +wave_file = wave.open(RECORD_FILE_NAME,"w") wave_file.setnchannels(2) wave_file.setsampwidth(4) -wave_file.setframerate(supportet_samplerates[1]) +wave_file.setframerate(supported_samplerates[1]) def read_samples(transfer): global sample_count global wave_file @@ -98,11 +114,10 @@ read_samples_cb = airspyhf_sample_block_cb_fn(read_samples) ret = libairspyhf.airspyhf_start(dev_p, airspyhf_sample_block_cb_fn(read_samples), None) print(f"airspyhf_start ret={ret}") -#ret = airspyhf.libairspyhf.py_cb_wrapper(dev_p) -#print(f"airspyhf_start ret={ret}") - - -ret = libairspyhf.airspyhf_set_freq(dev_p, 3865000) +if args.frequency == None: + ret = libairspyhf.airspyhf_set_freq(dev_p, 3865000) +else: + ret = libairspyhf.airspyhf_set_freq(dev_p, int(args.frequency)) print(f"airspyhf_set_freq ret={ret}") count = 0 @@ -116,6 +131,7 @@ except: ret = libairspyhf.airspyhf_stop(dev_p) print(f"airspyhf_stop ret={ret}") +time.sleep(1) #Not close for now ret = libairspyhf.close(dev_p) diff --git a/airspyhf_shedule.py b/airspyhf_shedule.py index e69de29..ad23e3d 100644 --- a/airspyhf_shedule.py +++ b/airspyhf_shedule.py @@ -0,0 +1,8 @@ +import os +from airspyhf import * +from ctypes import * +import time +import sys +import wave +import struct +import argparse
\ No newline at end of file @@ -1,3 +1,4 @@ +#!/usr/bin/python3 import os from airspyhf import * from ctypes import * @@ -110,10 +111,6 @@ print("closed: Returned %d"%(ret)) print(f"Total samples received {sample_count}") -libairspyhf.py_test() - -libairspyhf.py_test_cb(read_samples_cb) - wave_file.close() print("All is ok") diff --git a/test_class.py b/test_class.py new file mode 100755 index 0000000..35a6fd3 --- /dev/null +++ b/test_class.py @@ -0,0 +1,13 @@ +from airspyhf import * +from ctypes import * +import time +import sys +import wave +import struct +import argparse + +airspy = AirSpyHF() + +airspy.open(device_index=0) + +airspy.close()
\ No newline at end of file |