UNPKG

hackrf.js

Version:
791 lines 29.7 kB
"use strict"; /** * Main module, contains the USB interfacing code * and user-facing API * @module */ Object.defineProperty(exports, "__esModule", { value: true }); exports.HackrfDevice = exports.open = exports.listDevices = exports.defaultStreamOptions = void 0; const util_1 = require("util"); const usb_1 = require("usb"); const { LibUSBException, LIBUSB_ERROR_NOT_SUPPORTED, LIBUSB_ENDPOINT_OUT, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_TYPE_VENDOR, LIBUSB_RECIPIENT_DEVICE, LIBUSB_TRANSFER_TYPE_BULK, LIBUSB_TRANSFER_CANCELLED, } = usb_1.usb; const endpoint_1 = require("usb/dist/usb/endpoint"); const constants_1 = require("./constants"); const util_2 = require("./util"); exports.defaultStreamOptions = { transferCount: 4, transferBufferSize: 262144, }; const poll = (setup, endpoint, callback, options) => new Promise((resolve, reject) => { const opts = { ...exports.defaultStreamOptions, ...options }; const isOut = endpoint instanceof endpoint_1.OutEndpoint; const pendingTransfers = new Set(); let cancelled = false; const tryCancel = () => { if (cancelled) return; pendingTransfers.forEach(x => { try { x.cancel(); } catch (e) { } }); cancelled = true; }; let settled = false; let rejected = false; let reason; const wrapResolve = () => (settled = true, !isOut && tryCancel()); const wrapReject = (x) => (settled = true, rejected = true, reason = x, tryCancel()); const safeCall = (fn) => { if (settled) return; try { fn(); } catch (e) { wrapReject(e); } }; const doSettle = () => { if (settled && pendingTransfers.size === 0) rejected ? reject(reason) : resolve(); }; // allocate / fill buffers const tasks = []; for (let i = 0; !settled && i < opts.transferCount; i++) { const buffer = Buffer.alloc(opts.transferBufferSize); const array = new Int8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); if (isOut) { if (callback(array) === false) break; } tasks.push(submitTransfer); function submitTransfer() { const transfer = endpoint.makeTransfer(0, transferCallback).submit(buffer); pendingTransfers.add(transfer); function transferCallback(error, b, length) { if (cancelled && (error === null || error === void 0 ? void 0 : error.errno) === LIBUSB_TRANSFER_CANCELLED) error = undefined; if (!rejected && error) wrapReject(error); // potentially heavy callback... move to the next tick // to prevent starving the loop (setImmediate preserves order) settled ? inNextTick() : setImmediate(inNextTick); function inNextTick() { pendingTransfers.delete(transfer); safeCall(() => { if (callback(isOut ? array : array.subarray(0, length)) === false) wrapResolve(); }); safeCall(submitTransfer); doSettle(); } } } } // start the stream Promise.resolve(setup()).then(() => { tasks.forEach(submitTransfer => safeCall(submitTransfer)); doSettle(); }, reject); }); // libusb is not well-designed for the manual configuration case, // and we need to use internals. See tessel/node-usb#377 for context const getActiveConfig = (device) => device.__getConfigDescriptor().bConfigurationValue; function detachKernelDrivers(handle) { const { bNumInterfaces } = handle.__getConfigDescriptor(); for (let num = 0; num < bNumInterfaces; num++) { let active; try { active = handle.__isKernelDriverActive(num); } catch (e) { if (e instanceof LibUSBException && e.errno === LIBUSB_ERROR_NOT_SUPPORTED) return; throw e; } if (active) handle.__detachKernelDriver(num); } } function isValidDevice(device) { const { idVendor, idProduct } = device.deviceDescriptor; return idVendor === constants_1.USB_HACKRF_VID && Object.hasOwnProperty.call(constants_1.UsbBoardId, idProduct); } /** * Return info about each HackRF device present. */ async function* listDevices() { for (const device of (0, usb_1.getDeviceList)().filter(isValidDevice)) { const { idProduct, iSerialNumber } = device.deviceDescriptor; const info = { device, usbBoardId: idProduct }; if (iSerialNumber > 0) { try { device.open(false); info.serialNumber = await (0, util_1.promisify)(cb => device.getStringDescriptor(iSerialNumber, cb))(); } catch (e) { } finally { device.close(); } } yield info; } } exports.listDevices = listDevices; /** * Open the first device whose serial number ends with the passed suffix. * If no suffix is passed, open the first device. * * @param serialNumber Serial number suffix to match */ async function open(serialNumber) { var _a; if (serialNumber) { for await (const info of listDevices()) { if ((_a = info.serialNumber) === null || _a === void 0 ? void 0 : _a.endsWith(serialNumber)) return HackrfDevice.open(info.device); } } else { const device = (0, usb_1.findByIds)(constants_1.USB_HACKRF_VID, constants_1.UsbBoardId.HACKRF_ONE) || (0, usb_1.findByIds)(constants_1.USB_HACKRF_VID, constants_1.UsbBoardId.JAWBREAKER) || (0, usb_1.findByIds)(constants_1.USB_HACKRF_VID, constants_1.UsbBoardId.RAD1O); if (device) return HackrfDevice.open(device); } throw new util_2.HackrfError(constants_1.ErrorCode.NOT_FOUND); } exports.open = open; /** * Reference to an open HackRF device * * This is mostly a direct API to the USB interface. Call * [[close]] when no longer needed. * * Keep in mind some methods require a certain API version * to be implemented by your device's firmware; this is noted * in their documentation, and an `USB_API_VERSION` error will * be thrown if you attempt to use them. [[usbApiVersion]] * returns the version implemented by the firmware. It's * strongly recommended to upgrade your device's firmware to * the latest version to avoid problems and glitches. * * This API does strict validation of passed integers (they * should be integers and be in-range). Gains, in particular, * will be *rejected* instead of rounded down to the nearest * step. */ class HackrfDevice { constructor(handle, iface) { this._open = true; // DATA TRANSFERS this._streaming = false; this._stopRequested = false; this.handle = handle; this.iface = iface; this.outEndpoint = iface.endpoint(2 | LIBUSB_ENDPOINT_OUT); if (this.outEndpoint.transferType !== LIBUSB_TRANSFER_TYPE_BULK) throw new util_2.HackrfError(constants_1.ErrorCode.LIBUSB); this.inEndpoint = iface.endpoint(1 | LIBUSB_ENDPOINT_IN); if (this.inEndpoint.transferType !== LIBUSB_TRANSFER_TYPE_BULK) throw new util_2.HackrfError(constants_1.ErrorCode.LIBUSB); } /** * Open the passed USB device * * This function does **not** validate the device, * it's recommended to use the `open` module function * instead of this function directly. * * @param device USB device (must not be open) * @category Main */ static async open(device) { device.open(false); // FIXME: disabled because usb's API won't let us // if (getActiveConfig(device) !== USB_CONFIG_STANDARD) { detachKernelDrivers(device); await (0, util_1.promisify)(cb => device.setConfiguration(constants_1.USB_CONFIG_STANDARD, cb))(); // } detachKernelDrivers(device); const iface = device.interface(0); iface.claim(); try { if (getActiveConfig(device) !== constants_1.USB_CONFIG_STANDARD) throw new util_2.HackrfError(constants_1.ErrorCode.LIBUSB); return new HackrfDevice(device, iface); } catch (e) { await (0, util_1.promisify)(cb => iface.release(cb))(); device.close(); throw e; } } get open() { return this._open; } /** * Release resources and close the USB device * * Unless the device is used until process exit, this **must** be * called once when it's no longer needed. * * There must be no pending promises or an active stream when * calling this. After return, no more methods should be called * on this object. * * @category Main */ async close() { await (0, util_1.promisify)(cb => this.iface.release(cb))(); this.handle.close(); this._open = false; } /** * Version of the USB API implemented by the device's firmware * * In `0xAABB` form (`AA` = major, `BB` = minor). */ get usbApiVersion() { return this.handle.deviceDescriptor.bcdDevice; } usbApiRequired(version) { if (this.usbApiVersion < version) throw new util_2.HackrfError(constants_1.ErrorCode.USB_API_VERSION); } // CONTROL TRANSFERS controlTransferIn(bRequest, wValue, wIndex, length) { return (0, util_1.promisify)(cb => this.handle.controlTransfer(LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, bRequest, wValue, wIndex, length, cb))(); } controlTransferOut(bRequest, wValue, wIndex, data = Buffer.alloc(0)) { return (0, util_1.promisify)(cb => this.handle.controlTransfer(LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, bRequest, wValue, wIndex, data, cb))(); } async setTransceiverMode(value) { await this.controlTransferOut(constants_1.VendorRequest.SET_TRANSCEIVER_MODE, value, 0); } /** * Query the firmware version * * @category Device info */ async getVersionString() { const buf = await this.controlTransferIn(constants_1.VendorRequest.VERSION_STRING_READ, 0, 0, 255); return buf.toString('utf-8'); } /** * @category Device info */ async getBoardId() { const buf = await this.controlTransferIn(constants_1.VendorRequest.BOARD_ID_READ, 0, 0, 1); return (0, util_2.checkInLength)(buf, 1).readUInt8(); } /** * @category Device info */ async getBoardPartIdSerialNo() { const buf = await this.controlTransferIn(constants_1.VendorRequest.BOARD_PARTID_SERIALNO_READ, 0, 0, 24); (0, util_2.checkInLength)(buf, 24); const u32 = [0, 1, 2, 3, 4, 5].map(x => buf.readUInt32LE(x * 4)); return { partId: u32.slice(0, 2), serialNo: u32.slice(2, 6), }; } /** * @category IC */ async max2837_read(register) { const buf = await this.controlTransferIn(constants_1.VendorRequest.MAX2837_READ, 0, (0, util_2.checkMax2837Reg)(register), 2); return (0, util_2.checkInLength)(buf, 2).readUInt16LE(); } /** * @category IC */ async max2837_write(register, value) { await this.controlTransferOut(constants_1.VendorRequest.MAX2837_WRITE, (0, util_2.checkMax2837Value)(value), (0, util_2.checkMax2837Reg)(register)); } /** * @category IC */ async si5351c_read(register) { const buf = await this.controlTransferIn(constants_1.VendorRequest.SI5351C_READ, 0, (0, util_2.checkSi5351cReg)(register), 1); return (0, util_2.checkInLength)(buf, 1).readUInt8(); } /** * @category IC */ async si5351c_write(register, value) { await this.controlTransferOut(constants_1.VendorRequest.SI5351C_WRITE, (0, util_2.checkSi5351cValue)(value), (0, util_2.checkSi5351cReg)(register)); } /** * @category IC */ async rffc5071_read(register) { const buf = await this.controlTransferIn(constants_1.VendorRequest.RFFC5071_READ, 0, (0, util_2.checkRffc5071Reg)(register), 2); return (0, util_2.checkInLength)(buf, 2).readUInt16LE(); } /** * @category IC */ async rffc5071_write(register, value) { await this.controlTransferOut(constants_1.VendorRequest.RFFC5071_WRITE, (0, util_2.checkRffc5071Value)(value), (0, util_2.checkRffc5071Reg)(register)); } /** * @category Flash & CPLD */ async spiflash_erase() { await this.controlTransferOut(constants_1.VendorRequest.SPIFLASH_ERASE, 0, 0); } /** * @category Flash & CPLD */ async spiflash_write(address, data) { (0, util_2.checkSpiflashAddress)(address); await this.controlTransferOut(constants_1.VendorRequest.SPIFLASH_WRITE, address >>> 16, address & 0xFFFF, data); } /** * @category Flash & CPLD */ async spiflash_read(address, length) { (0, util_2.checkSpiflashAddress)(address); const buf = await this.controlTransferIn(constants_1.VendorRequest.SPIFLASH_READ, address >>> 16, address & 0xFFFF, length); return (0, util_2.checkInLength)(buf, length); } /** * TODO * * Requires USB API 1.3. * * @category Flash & CPLD */ async spiflash_getStatus() { this.usbApiRequired(0x0103); const buf = await this.controlTransferIn(constants_1.VendorRequest.SPIFLASH_STATUS, 0, 0, 2); return (0, util_2.checkInLength)(buf, 1); // FIXME } /** * TODO * * Requires USB API 1.3. * * @category Flash & CPLD */ async spiflash_clearStatus() { this.usbApiRequired(0x0103); await this.controlTransferOut(constants_1.VendorRequest.SPIFLASH_CLEAR_STATUS, 0, 0); } /** * Set baseband filter bandwidth in Hz * * Possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz * * @category Radio control */ async setBasebandFilterBandwidth(freqHz) { (0, util_2.checkBasebandFilterBw)((0, util_2.checkU32)(freqHz)); await this.controlTransferOut(constants_1.VendorRequest.BASEBAND_FILTER_BANDWIDTH_SET, freqHz & 0xffff, freqHz >>> 16); } /** * Set the tuning frequency * * @category Radio control */ async setFrequency(freqHz) { (0, util_2.checkFreq)(freqHz); // convert Freq Hz 64bits to Freq MHz (32bits) & Freq Hz (32bits) const FREQ_ONE_MHZ = 1000 * 1000; const data = Buffer.alloc(8); data.writeUInt32LE(freqHz / FREQ_ONE_MHZ, 0); data.writeUInt32LE(freqHz % FREQ_ONE_MHZ, 4); await this.controlTransferOut(constants_1.VendorRequest.SET_FREQ, 0, 0, data); } /** * Set the tuning frequency (raw version) * * @param iFreqHz intermediate frequency * @param loFreqHz front-end local oscillator frequency * @param path image rejection filter path * @category Radio control */ async setFrequencyExplicit(iFreqHz, loFreqHz, path) { (0, util_2.checkIFreq)(iFreqHz); if (path !== constants_1.RfPathFilter.BYPASS) (0, util_2.checkLoFreq)(loFreqHz); if ((0, util_2.checkU32)(path) > 2) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); const data = Buffer.alloc(8 + 8 + 1); data.writeBigUInt64LE(BigInt(iFreqHz), 0); data.writeBigUInt64LE(BigInt(loFreqHz), 8); data.writeUInt8(path, 16); await this.controlTransferOut(constants_1.VendorRequest.SET_FREQ_EXPLICIT, 0, 0, data); } /** * Set the sample rate (raw version) * * You should probably use [[setSampleRate]] instead of this * function. * * For anti-aliasing, the baseband filter bandwidth is automatically set to the * widest available setting that is no more than 75% of the sample rate. This * happens every time the sample rate is set. If you want to override the * baseband filter selection, you must do so after setting the sample rate. * * 2-20Mhz - as a fraction, i.e. freq 20000000 divider 2 -> 10Mhz * * @category Radio control */ async setSampleRateManual(freqHz, divider) { const data = Buffer.alloc(8); data.writeUInt32LE(freqHz, 0); data.writeUInt32LE(divider, 4); await this.controlTransferOut(constants_1.VendorRequest.SAMPLE_RATE_SET, 0, 0, data); } /** * Set the sample rate * * For anti-aliasing, the baseband filter bandwidth is automatically set to the * widest available setting that is no more than 75% of the sample rate. This * happens every time the sample rate is set. If you want to override the * baseband filter selection, you must do so after setting the sample rate. * * @param freqHz frequency in Hz, 2-20MHz (double) * * @category Radio control */ async setSampleRate(freqHz) { return this.setSampleRateManual(...(0, util_2.calcSampleRate)(freqHz)); } /** * Enable / disable RX/TX RF external amplifier * * @category Radio control */ async setAmpEnable(value) { await this.controlTransferOut(constants_1.VendorRequest.AMP_ENABLE, Number(value), 0); } /** * Set RX LNA (IF) gain, 0-40dB in 8dB steps * * @category Radio control */ async setLnaGain(gainDb) { if ((0, util_2.checkU32)(gainDb) > 40 || gainDb % 8) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); const buf = await this.controlTransferIn(constants_1.VendorRequest.SET_LNA_GAIN, 0, gainDb, 1); if (buf.length != 1 || !buf.readUInt8()) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); } /** * Set RX VGA (baseband) gain, 0-62dB in 2dB steps * * @category Radio control */ async setVgaGain(gainDb) { if ((0, util_2.checkU32)(gainDb) > 62 || gainDb % 2) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); const buf = await this.controlTransferIn(constants_1.VendorRequest.SET_VGA_GAIN, 0, gainDb, 1); if (buf.length != 1 || !buf.readUInt8()) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); } /** * Set TX VGA (IF) gain, 0-47dB in 1dB steps * * @category Radio control */ async setTxVgaGain(gainDb) { if ((0, util_2.checkU32)(gainDb) > 47) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); const buf = await this.controlTransferIn(constants_1.VendorRequest.SET_TXVGA_GAIN, 0, gainDb, 1); if (buf.length != 1 || !buf.readUInt8()) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); } /** * Antenna port power control * * @category Radio control */ async setAntennaEnable(value) { await this.controlTransferOut(constants_1.VendorRequest.ANTENNA_ENABLE, Number(value), 0); } /** * Enable / disable hardware sync * * Multiple boards can be made to syncronize * their USB transfers through a GPIO connection * between them. * * Requires USB API 1.2. * * @category Radio control */ async setHwSyncMode(value) { this.usbApiRequired(0x0102); await this.controlTransferOut(constants_1.VendorRequest.SET_HW_SYNC_MODE, Number(value), 0); } /** * Reset the device * * Requires USB API 1.2. * * @category Main */ async reset() { this.usbApiRequired(0x0102); await this.controlTransferOut(constants_1.VendorRequest.RESET, 0, 0); } /** * Initialize sweep mode * * Requires USB API 1.2. * * @param ranges is a list of `[start, stop]` pairs of frequencies in MHz, * no more than [[MAX_SWEEP_RANGES]] entries. * @param numBytes the number of sample bytes to capture after each tuning. * @param stepWidth the width in Hz of the tuning step. * @param offset number of Hz added to every tuning frequency. * Use to select center frequency based on the expected usable bandwidth. * @category Radio control */ async initSweep(ranges, numBytes, stepWidth, offset, style) { this.usbApiRequired(0x0102); if (!(ranges.length >= 1 && ranges.length <= constants_1.MAX_SWEEP_RANGES)) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); if (numBytes % constants_1.BYTES_PER_BLOCK || numBytes < constants_1.BYTES_PER_BLOCK) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); if (stepWidth < 1) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); if ((0, util_2.checkU32)(style) > 1) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); const data = Buffer.alloc(9 + ranges.length * 4); data.writeUInt32LE((0, util_2.checkU32)(stepWidth), 0); data.writeUInt32LE((0, util_2.checkU32)(offset), 4); data.writeUInt8(style, 8); ranges.forEach(([start, stop], i) => { data.writeUInt16LE((0, util_2.checkU16)(start), 9 + i * 4); data.writeUInt16LE((0, util_2.checkU16)(stop), 9 + i * 4 + 2); }); (0, util_2.checkU32)(numBytes); await this.controlTransferOut(constants_1.VendorRequest.INIT_SWEEP, numBytes & 0xffff, (numBytes >>> 16) & 0xffff, data); } /** * Retrieve list of Opera Cake board addresses (uint8, terminated by 0) * * Requires USB API 1.2. * * @category Opera Cake */ async getOperacakeBoards() { this.usbApiRequired(0x0102); const buf = await this.controlTransferIn(constants_1.VendorRequest.OPERACAKE_GET_BOARDS, 0, 0, 8); return Array.from((0, util_2.checkInLength)(buf, 8)); } /** * Set Opera Cake ports * * Requires USB API 1.2. * * @category Opera Cake */ async setOperacakePorts(address, portA, portB) { this.usbApiRequired(0x0102); if ((0, util_2.checkU32)(portA) > constants_1.OperacakePorts.PB4 || (0, util_2.checkU32)(portB) > constants_1.OperacakePorts.PB4) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); // Check which side PA and PB are on if ((portA <= constants_1.OperacakePorts.PA4 && portB <= constants_1.OperacakePorts.PA4) || (portA > constants_1.OperacakePorts.PA4 && portB > constants_1.OperacakePorts.PA4)) throw new util_2.HackrfError(constants_1.ErrorCode.INVALID_PARAM); await this.controlTransferOut(constants_1.VendorRequest.OPERACAKE_SET_PORTS, (0, util_2.checkU8)(address), portA | (portB << 8)); } /** * Set Opera Cake [frequency-antenna ranges](https://github.com/mossmann/hackrf/wiki/Opera-Cake#opera-glasses) * * Requires USB API 1.3. * * @category Opera Cake */ async setOperacakeRanges(ranges) { this.usbApiRequired(0x0103); await this.controlTransferOut(constants_1.VendorRequest.OPERACAKE_SET_RANGES, 0, 0, ranges); } /** * Test GPIO functionality of an Opera Cake * * Returns test result (uint16) * * Requires USB API 1.3. * * @category Opera Cake */ async operacakeGpioTest(address) { this.usbApiRequired(0x0103); const buf = await this.controlTransferIn(constants_1.VendorRequest.OPERACAKE_GPIO_TEST, address, 0, 2); return (0, util_2.checkInLength)(buf, 1); // FIXME } /** * Enable / disable clock output through CLKOUT * * Requires USB API 1.3. * * @category Radio control */ async setClkoutEnable(value) { this.usbApiRequired(0x0103); await this.controlTransferOut(constants_1.VendorRequest.CLKOUT_ENABLE, Number(value), 0); } // Disabled for now, see https://github.com/mossmann/hackrf/issues/609 // /** // * Returns crc32 (uint32) // * // * Requires USB API 1.3. // * // * @category Flash & CPLD // */ // async cpld_checksum() { // this.usbApiRequired(0x0103) // const buf = await this.controlTransferIn(VendorRequest.CPLD_CHECKSUM, 0, 0, 4) // return checkInLength(buf, 4).readUInt32LE() // } /** * Enable / disable PortaPack display * * Requires USB API 1.4. * * @category Radio control */ async setUiEnable(value) { this.usbApiRequired(0x0104); await this.controlTransferOut(constants_1.VendorRequest.UI_ENABLE, Number(value), 0); } /** * Returns `true` if there's an active stream. */ get streaming() { return this._streaming; } async _lockStream(callback) { if (this._streaming) throw new util_2.HackrfError(constants_1.ErrorCode.BUSY); try { this._streaming = true; await callback(); } finally { this._streaming = false; } } /** * Requests stopping the active stream (if there is one) * * Calling this has the same effect as returning `false` * the next time the callback gets called. Note that the * stream doesn't finish instantly, you still need to * wait for the promise to end. This is merely a convenience * function. * * @category Main */ requestStop() { // FIXME: this waits till the next callback; for RX we could do a bit better this._stopRequested = true; } async _stream(mode, endpoint, callback, options) { await this._lockStream(async () => { this._stopRequested = false; try { const setup = () => this.setTransceiverMode(mode); await poll(setup, endpoint, array => { if (this._stopRequested) return false; return callback(array); }, options); } finally { await this.setTransceiverMode(constants_1.TransceiverMode.OFF); } }); } /** * Put the radio in TX mode and stream I/Q samples * * The supplied callback will be regularly called with an * `Int8Array` buffer to fill before return. Every two * values of the buffer form an I/Q sample. Different * buffers may be passed or reused, so avoid storing * references to them after return. * * To request ending the stream, return `false` from the * callback or use [[requestStop]] (the callback will no * longer be called and the current buffer will not be * transmitted). Any transfer / callback error rejects * the promise and cancels all transfers. The promise won't * settle until all transfers are finished, regardless of * whether the stream is ended or errored. * * This throws if there's another stream in progress. * * @category Main */ async transmit(callback, options) { await this._stream(constants_1.TransceiverMode.TRANSMIT, this.outEndpoint, callback, options); } /** * Put the radio in RX mode and stream I/Q samples * * The supplied callback will be regularly called with an * `Int8Array` buffer. Every two values of the buffer * form an I/Q sample. The buffer may be overwritten * later, so avoid storing any reference to it; instead * make a copy of the data if needed. * * To request ending the stream, return `false` from the * callback or use [[requestStop]] (the callback will no * longer be called). Any transfer / callback error rejects * the promise and cancels all transfers. The promise won't * settle until all transfers are finished, regardless of * whether the stream is ended or errored. * * This throws if there's another stream in progress. * * @category Main */ async receive(callback, options) { await this._stream(constants_1.TransceiverMode.RECEIVE, this.inEndpoint, callback, options); } /** * Put the radio in sweep RX mode and stream I/Q samples * * Like [[receive]], but with frequency sweep active. * You should call [[initSweep]] first. * * Requires USB API 1.4. * * @category Main */ async sweepReceive(callback, options) { this.usbApiRequired(0x0104); await this._stream(constants_1.TransceiverMode.RX_SWEEP, this.inEndpoint, callback, options); } /** * Put the radio in CPLD firmware upgrade mode and * write the payload * * This throws if there's another stream in progress. * * The device will need to be reset after this. * * @category Flash & CPLD */ async cpld_write(data, chunkSize = 512) { await this._lockStream(async () => { await this.setTransceiverMode(constants_1.TransceiverMode.CPLD_UPDATE); for (let i = 0; i < data.length; i += chunkSize) await (0, util_1.promisify)(cb => this.outEndpoint.transfer(data.subarray(i, i + chunkSize), cb))(); }); } } exports.HackrfDevice = HackrfDevice; //# sourceMappingURL=interface.js.map