├── LICENSE ├── README.md └── hackrf ├── device.go ├── exports.c ├── exports.h ├── hackrf.go └── hackrf_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2017, Samuel Stauffer 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the author nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HackRF API 2 | ========== 3 | 4 | This package wraps libhackrf using cgo. 5 | 6 | Documentation: https://godoc.org/github.com/samuel/go-hackrf/hackrf 7 | 8 | Status 9 | ------ 10 | 11 | A number of functions are not yet implemented, but the main ones for doing 12 | RX and TX are. 13 | -------------------------------------------------------------------------------- /hackrf/device.go: -------------------------------------------------------------------------------- 1 | package hackrf 2 | 3 | // #include 4 | // #include 5 | // #include "exports.h" 6 | import "C" 7 | 8 | import ( 9 | "sync" 10 | "unsafe" 11 | ) 12 | 13 | var ( 14 | cbMu sync.RWMutex 15 | callbacks []*callbackContext 16 | ) 17 | 18 | type Device struct { 19 | cdev *C.hackrf_device 20 | callbacks []int 21 | } 22 | 23 | type Callback func(buf []byte) error 24 | 25 | type callbackContext struct { 26 | cb Callback 27 | tx bool 28 | dev *Device 29 | } 30 | 31 | //export cbGo 32 | func cbGo(transfer *C.hackrf_transfer, tx C.int) C.int { 33 | var cbIdx int 34 | if tx == 0 { 35 | cbIdx = int(uintptr(transfer.rx_ctx)) 36 | } else { 37 | cbIdx = int(uintptr(transfer.tx_ctx)) 38 | } 39 | cbMu.RLock() 40 | if cbIdx >= len(callbacks) { 41 | cbMu.RUnlock() 42 | return -1 43 | } 44 | ctx := callbacks[cbIdx] 45 | cbMu.RUnlock() 46 | if ctx == nil { 47 | return -1 48 | } 49 | n := int(transfer.valid_length) 50 | goBuf := (*[1 << 30]byte)(unsafe.Pointer(transfer.buffer))[:n:n] 51 | if err := ctx.cb(goBuf); err != nil { 52 | return -1 53 | } 54 | return 0 55 | } 56 | 57 | // Open returns the HackRF device which provides access to the hardware. 58 | func Open() (*Device, error) { 59 | var d Device 60 | if r := C.hackrf_open(&d.cdev); r != C.HACKRF_SUCCESS { 61 | return nil, toError(r) 62 | } 63 | return &d, nil 64 | } 65 | 66 | func (d *Device) Close() error { 67 | e := toError(C.hackrf_close(d.cdev)) 68 | if e == nil { 69 | d.cdev = nil 70 | } 71 | return e 72 | } 73 | 74 | func (d *Device) Version() (string, error) { 75 | ver := (*C.char)(C.malloc(128)) 76 | defer C.free(unsafe.Pointer(ver)) 77 | if r := C.hackrf_version_string_read(d.cdev, ver, 128); r != C.HACKRF_SUCCESS { 78 | return "", toError(r) 79 | } 80 | return C.GoString(ver), nil 81 | } 82 | 83 | // StartRX starts sampling sending the IQ samples to the callback. 84 | func (d *Device) StartRX(cb Callback) error { 85 | cbIx := d.registerCallback(&callbackContext{ 86 | cb: cb, 87 | tx: false, 88 | dev: d, 89 | }) 90 | return toError(C.hackrf_start_rx(d.cdev, (*[0]byte)(unsafe.Pointer(C.rxCBPtr)), unsafe.Pointer(uintptr(cbIx)))) 91 | } 92 | 93 | func (d *Device) StopRX() error { 94 | return toError(C.hackrf_stop_rx(d.cdev)) 95 | } 96 | 97 | func (d *Device) StartTX(cb Callback) error { 98 | cbIx := d.registerCallback(&callbackContext{ 99 | cb: cb, 100 | tx: true, 101 | dev: d, 102 | }) 103 | return toError(C.hackrf_start_tx(d.cdev, (*[0]byte)(unsafe.Pointer(C.rxCBPtr)), unsafe.Pointer(uintptr(cbIx)))) 104 | } 105 | 106 | func (d *Device) StopTX() error { 107 | return toError(C.hackrf_stop_tx(d.cdev)) 108 | } 109 | 110 | func (d *Device) registerCallback(ctx *callbackContext) int { 111 | cbMu.Lock() 112 | cbIx := -1 113 | for i, cb := range callbacks { 114 | if cb == nil { 115 | cbIx = i 116 | callbacks[i] = ctx 117 | break 118 | } 119 | } 120 | if cbIx < 0 { 121 | cbIx = len(callbacks) 122 | callbacks = append(callbacks, ctx) 123 | } 124 | cbMu.Unlock() 125 | d.callbacks = append(d.callbacks, cbIx) 126 | return cbIx 127 | } 128 | 129 | func (d *Device) SetFreq(freqHz uint64) error { 130 | return toError(C.hackrf_set_freq(d.cdev, C.uint64_t(freqHz))) 131 | } 132 | 133 | // extern ADDAPI int ADDCALL hackrf_set_freq_explicit(hackrf_device* device, 134 | // const uint64_t if_freq_hz, const uint64_t lo_freq_hz, 135 | // const enum rf_path_filter path); 136 | 137 | // SetSampleRateManual sets the sample rate in hz. 138 | // Preferred rates are 8, 10, 12.5, 16, 20MHz due to less jitter. 139 | func (d *Device) SetSampleRateManual(freqHz, divider int) error { 140 | return toError(C.hackrf_set_sample_rate_manual(d.cdev, C.uint32_t(freqHz), C.uint32_t(divider))) 141 | } 142 | 143 | // SetSampleRate sets the sample rate in hz. 144 | // Preferred rates are 8, 10, 12.5, 16, 20MHz due to less jitter. 145 | func (d *Device) SetSampleRate(freqHz float64) error { 146 | return toError(C.hackrf_set_sample_rate(d.cdev, C.double(freqHz))) 147 | } 148 | 149 | // SetBasebandFilterBandwidth sets the baseband bandwidth. 150 | // Possible values are 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz. 151 | func (d *Device) SetBasebandFilterBandwidth(hz int) error { 152 | return toError(C.hackrf_set_baseband_filter_bandwidth(d.cdev, C.uint32_t(hz))) 153 | } 154 | 155 | // SetAmpEnable enables or disables the external RX/TX RF amplifier. 156 | func (d *Device) SetAmpEnable(value bool) error { 157 | var v C.uint8_t 158 | if value { 159 | v = 1 160 | } 161 | return toError(C.hackrf_set_amp_enable(d.cdev, v)) 162 | } 163 | 164 | // SetLNAGain sets the gain for the RX low-noise amplifier (IF). 165 | // Range 0-40 step 8db 166 | func (d *Device) SetLNAGain(value int) error { 167 | return toError(C.hackrf_set_lna_gain(d.cdev, C.uint32_t(value))) 168 | } 169 | 170 | // SetVGAGain sets the gain for the RX variable gain amplifier (baseband). 171 | // Range 0-62 step 2db 172 | func (d *Device) SetVGAGain(value int) error { 173 | return toError(C.hackrf_set_vga_gain(d.cdev, C.uint32_t(value))) 174 | } 175 | 176 | // SetTXVGAGain sets the gain for the TX variable gain amplifier (IF). 177 | // Range 0-47 step 1db 178 | func (d *Device) SetTXVGAGain(value int) error { 179 | return toError(C.hackrf_set_txvga_gain(d.cdev, C.uint32_t(value))) 180 | } 181 | 182 | // SetAntennaEnable enables ot disables antenna port power 183 | func (d *Device) SetAntennaEnable(enabled bool) error { 184 | var value C.uint8_t 185 | if enabled { 186 | value = 1 187 | } 188 | return toError(C.hackrf_set_antenna_enable(d.cdev, C.uint8_t(value))) 189 | } 190 | 191 | // ComputeBasebandFilterBWRoundDownLT computse the nearest freq for bw filter 192 | // (manual filter) 193 | func ComputeBasebandFilterBWRoundDownLT(bandwidthHz int) int { 194 | return int(C.hackrf_compute_baseband_filter_bw_round_down_lt(C.uint32_t(bandwidthHz))) 195 | } 196 | 197 | // ComputeBasebandFilterBWRoundDownLT computes the best default value 198 | // depending on sample rate (auto filter) 199 | func ComputeBasebandFilterBW(bandwidthHz int) int { 200 | return int(C.hackrf_compute_baseband_filter_bw(C.uint32_t(bandwidthHz))) 201 | } 202 | -------------------------------------------------------------------------------- /hackrf/exports.c: -------------------------------------------------------------------------------- 1 | #include "exports.h" 2 | 3 | int rxCB(hackrf_transfer* transfer) { 4 | return cbGo(transfer, 0); 5 | } 6 | 7 | int txCB(hackrf_transfer* transfer) { 8 | return cbGo(transfer, 1); 9 | } 10 | 11 | const hackrf_sample_block_cb_fn *rxCBPtr = (hackrf_sample_block_cb_fn*)&rxCB; 12 | const hackrf_sample_block_cb_fn *txCBPtr = (hackrf_sample_block_cb_fn*)&txCB; 13 | -------------------------------------------------------------------------------- /hackrf/exports.h: -------------------------------------------------------------------------------- 1 | #ifndef _EXPORTS_H_ 2 | #define _EXPORTS_H_ 1 3 | 4 | #include 5 | 6 | extern const hackrf_sample_block_cb_fn *rxCBPtr; 7 | extern const hackrf_sample_block_cb_fn *txCBPtr; 8 | 9 | extern int cbGo(hackrf_transfer* transfer, int tx); 10 | int rxCB(hackrf_transfer* transfer); 11 | int txCB(hackrf_transfer* transfer); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /hackrf/hackrf.go: -------------------------------------------------------------------------------- 1 | // Package hackrf provides an interface to the HackRF SDR hardware. 2 | // 3 | // This package wraps libhackrf using cgo. 4 | package hackrf 5 | 6 | // #cgo darwin CFLAGS: -I/usr/local/include 7 | // #cgo darwin LDFLAGS: -L/usr/local/lib 8 | // #cgo LDFLAGS: -lhackrf 9 | // #include 10 | import "C" 11 | import ( 12 | "errors" 13 | "fmt" 14 | "unsafe" 15 | ) 16 | 17 | var ( 18 | ErrInvalidParam = errors.New("hackrf: invalid param") 19 | ErrNotFound = errors.New("hackrf: not found") 20 | ErrBusy = errors.New("hackrf: busy") 21 | ErrNoMem = errors.New("hackrf: no mem") 22 | ErrLibUSB = errors.New("hackrf: libusb error") 23 | ErrThread = errors.New("hackrf: thread error") 24 | ErrStreamingThreadErr = errors.New("hackrf: streaming thread error") 25 | ErrStreamingStopped = errors.New("hackrf: streaming stopped") 26 | ErrStreamingExitCalled = errors.New("hackrf: streaming exit called") 27 | ErrOther = errors.New("hackrf: other error") 28 | ) 29 | 30 | type ErrUnknown int 31 | 32 | func (e ErrUnknown) Error() string { 33 | return fmt.Sprintf("hackrf: unknown error %d", int(e)) 34 | } 35 | 36 | // Init must be called once at the start of the program. 37 | func Init() error { 38 | return toError(C.hackrf_init()) 39 | } 40 | 41 | // Exit should be called once at the end of the program. 42 | func Exit() error { 43 | return toError(C.hackrf_exit()) 44 | } 45 | 46 | func toError(r C.int) error { 47 | if r == C.HACKRF_SUCCESS { 48 | return nil 49 | } 50 | switch r { 51 | case C.HACKRF_ERROR_INVALID_PARAM: 52 | return ErrInvalidParam 53 | case C.HACKRF_ERROR_NOT_FOUND: 54 | return ErrNotFound 55 | case C.HACKRF_ERROR_BUSY: 56 | return ErrBusy 57 | case C.HACKRF_ERROR_NO_MEM: 58 | return ErrNoMem 59 | case C.HACKRF_ERROR_LIBUSB: 60 | return ErrLibUSB 61 | case C.HACKRF_ERROR_THREAD: 62 | return ErrThread 63 | case C.HACKRF_ERROR_STREAMING_THREAD_ERR: 64 | return ErrStreamingThreadErr 65 | case C.HACKRF_ERROR_STREAMING_STOPPED: 66 | return ErrStreamingStopped 67 | case C.HACKRF_ERROR_STREAMING_EXIT_CALLED: 68 | return ErrStreamingExitCalled 69 | case C.HACKRF_ERROR_OTHER: 70 | return ErrOther 71 | } 72 | return ErrUnknown(int(r)) 73 | } 74 | 75 | func LibraryVersion() string { 76 | return C.GoString(C.hackrf_library_version()) 77 | } 78 | 79 | func LibraryRelease() string { 80 | return C.GoString(C.hackrf_library_release()) 81 | } 82 | 83 | type USBBoardID uint16 84 | 85 | const ( 86 | USBBoardIDJawbreaker USBBoardID = 0x604B 87 | USBBoardIDHackRFOne USBBoardID = 0x6089 88 | USBBoardIDRad1o USBBoardID = 0xCC15 89 | USBBoardIDInvalid USBBoardID = 0xFFFF 90 | ) 91 | 92 | func (u USBBoardID) String() string { 93 | switch u { 94 | case USBBoardIDJawbreaker: 95 | return "Jawbreaker" 96 | case USBBoardIDHackRFOne: 97 | return "HackRF One" 98 | case USBBoardIDRad1o: 99 | return "rad1o" 100 | case USBBoardIDInvalid: 101 | return "Invalid Board ID" 102 | } 103 | return fmt.Sprintf("Unknown Board ID %04x", uint16(u)) 104 | } 105 | 106 | type DeviceInfo struct { 107 | SerialNumber string 108 | USBBoardID USBBoardID 109 | USBDeviceIndex int 110 | } 111 | 112 | func DeviceList() ([]*DeviceInfo, error) { 113 | clist := C.hackrf_device_list() 114 | 115 | if clist.devicecount < 1 { 116 | return nil, nil 117 | } 118 | fmt.Printf("%d devices\n", clist.devicecount) 119 | 120 | serials := (*[1 << 30](*C.char))(unsafe.Pointer(clist.serial_numbers))[:clist.devicecount:clist.devicecount] 121 | usbBoardIDs := (*[1 << 30](C.int))(unsafe.Pointer(clist.usb_board_ids))[:clist.devicecount:clist.devicecount] 122 | usbDeviceIndexes := (*[1 << 30](C.int))(unsafe.Pointer(clist.usb_device_index))[:clist.devicecount:clist.devicecount] 123 | 124 | devices := make([]*DeviceInfo, clist.devicecount) 125 | for i := 0; i < int(clist.devicecount); i++ { 126 | devices[i] = &DeviceInfo{ 127 | SerialNumber: C.GoString(serials[i]), 128 | USBBoardID: USBBoardID(usbBoardIDs[i]), 129 | USBDeviceIndex: int(usbDeviceIndexes[i]), 130 | } 131 | } 132 | 133 | return devices, nil 134 | } 135 | -------------------------------------------------------------------------------- /hackrf/hackrf_test.go: -------------------------------------------------------------------------------- 1 | package hackrf 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestHackRF(t *testing.T) { 10 | if err := Init(); err != nil { 11 | t.Fatal(err) 12 | } 13 | defer Exit() 14 | dev, err := Open() 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | defer dev.Close() 19 | if ver, err := dev.Version(); err != nil { 20 | t.Fatal(err) 21 | } else { 22 | t.Logf("Version: %s", ver) 23 | } 24 | total := 0 25 | if err := dev.StartRX(func(buf []byte) error { 26 | total += len(buf) 27 | return nil 28 | }); err != nil { 29 | t.Fatal(err) 30 | } 31 | time.Sleep(time.Second) 32 | if err := dev.StopRX(); err != nil { 33 | t.Fatal(err) 34 | } 35 | fmt.Printf("%d bytes\n", total) 36 | } 37 | --------------------------------------------------------------------------------