JS port of libhackrf using usb. This module allows you to control HackRF devices (i.e. HackRF One, Jawbreaker, Rad1o) from Node.js.
Keep in mind this is mostly a direct API to the USB interface, so it's low level.
npm install hackrf.js
💡 Examples  • 📚 API reference
Use listDevices
to list HackRF devices, or open
to open the first device:
import { listDevices, open, UsbBoardId } from 'hackrf.js'
for await (const info of listDevices()) {
console.log(`Found ${UsbBoardId[info.usbBoardId]}`)
console.log(`Serial: ${info.serialNumber}`)
}
const device = await open()
To select among multiple devices, open
can take a serial number or suffix:
const device = await open('41fa')
If successful, a HackrfDevice
instance is returned.
Then you'd usually configure the parameters:
await device.setSampleRate(20e6) // 20 Msps
await device.setFrequency(2451e6) // tune to 2.451 GHz
await device.setAmpEnable(false) // RF amplifier = off
// for RX only
await device.setLnaGain(8) // IF gain = 8dB
await device.setVgaGain(12) // BB gain = 12dB
// for TX only
await device.setTxVgaGain(8) // IF gain = 8dB
Finally, use receive
, sweepReceive
or transmit
to start streaming I/Q samples. These methods accept a callback that receives an Int8Array
to fill (TX) or read (RX):
await device.receive(array => {
// TODO: Process the samples in `array`
// - Every 2 items form an I/Q sample
// - int8 means the range is -128 to +127
})
The array's buffer may be reused (overwritten) later, so avoid storing any references to it. Instead, copy the data somewhere else.
To request ending the stream, return false
from the callback (or call device.requestStop()
which has the same effect).
If you no longer need the device, you must call device.close()
to release it (the GC will not do this automatically). This is not needed if the process is going to exit.
See the reference for the full API.
-
Gains go in steps: for instance,
setLnaGain
only allows multiples of 8dB. Non-multiples or out of range values will be rejected, not rounded. -
When transmitting, you should round or dither values before setting them on
Int8Array
. You should also check for out of range values. -
In the event of a Control+C, the process will exit and the radio will not be stopped if there was a transfer in progress. To avoid this, handle Control+C as necessary; for simple programs, calling
requestStop()
might be enough:process.on('SIGINT', () => device.requestStop())
Motivations for writing a port (versus using node-hackrf binary bindings) include:
- Expose the complete API; better control
- No native module / dependency, we rely on usb for everything
- We benefit from its prebuilds & support
- Integrate correctly with the event loop
- Avoid blocking the loop for i.e. control transfers
- Avoid spinning up additional threads to transfer data
- More natural & safe user-facing API
- Can be useful for hackers or learning USB