Skip to content

Hacked node-speaker exports and other imports here to get examples working #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions examples/acceptance_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Configure a HackRF as test rig, another as test subject (DUT) and run tests interactively
*/

import { DeviceInfo, listDevices, UsbBoardId, visible_hackrfs, scan_hackrfs } from '../lib'
import { run_test_sequence } from './test_sequence'

function timeout(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function await_hackrf(device: string) {
process.stdout.write(`Please connect ${device}: `);
for (;;) {
for await (const device_change of scan_hackrfs()) {
if (device_change.added) {
console.log(`\nPlugged in: ${device_change.added.serialNumber}`)
process.stdout.write("\n");
return device_change.added;
}
/* Ignore removals for now
else if (device_change.removed) {
console.log(`Unplugged: ${device_change.removed.serialNumber}`)
return undefined;
}
*/
}

await timeout(1000); // Sleep for a second before trying again
}
}

async function await_rig()
{
// Do the initial scan to see what's connected
for await (const device_change of scan_hackrfs()) { scan_hackrfs(); }

while (visible_hackrfs.length > 1) {
process.stdout.write(`${visible_hackrfs.length} HackRFs connected, unplug all but the test controller please\r`)
await timeout(1000); // Sleep for a second before trying again
for await (const device_change of scan_hackrfs()) { scan_hackrfs(); }
}

if (visible_hackrfs.length == 1)
{
console.log(`Detected ${visible_hackrfs[0].serialNumber} as test controller`)
return visible_hackrfs[0];
}
return await_hackrf("test controller HackRF");
}

async function await_dut()
{
return await_hackrf("HackRF to be tested");
}

async function main() {

var rig = await await_rig();
for (;;) {
var dut = await await_dut();
if (!dut) continue;

console.log(`Detected ${dut.serialNumber} as DUT`)

await run_test_sequence(rig, dut)
break; // REVISIT: Just one run for now :)
}
process.exit(0)
}
main()
4 changes: 2 additions & 2 deletions examples/fm_play.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* stereo, etc). Requires the ffmpeg tool to be present.
*/

import { open } from '..'
import { open } from '../lib'
import { once, EventEmitter } from 'events'
import { promises as fsPromises } from 'fs'
import { Readable } from 'stream'
Expand Down Expand Up @@ -56,7 +56,7 @@ async function main() {

console.log(`Transmitting at ${(carrierFrequency/1e6).toFixed(2)}MHz...`)
process.on('SIGINT', () => device.requestStop())
const transmission = device.transmit(array => {
const transmission = device.transmit((array : Int8Array) => {
if (audioBuffer.complete && audioBuffer.queueSize === 0)
return false
const samples = array.length / 2
Expand Down
20 changes: 13 additions & 7 deletions examples/fm_receiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@
* (SDR - audio card) but it should be functional.
*/

import { open } from '..'
import Speaker = require('speaker')
import { open } from '../lib'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changing .. to ../lib shouldn't be necessary to run the examples, as long as you've ran npm run build first and you have compiled files in dist 🤔

Copy link
Owner

@mildsunrise mildsunrise Sep 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw! these examples are meant to be run with ts-node or similar

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. New to the ecosystem and didn't have any build instructions :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also; opening in vscode is disappointing; that guesses that the build instructions are just to run tsc, so "Start Debugging" doesn't work

import Speaker from 'speaker'

async function main() {
const fs = 1200e3
const tuneOffset = -120e3
const carrierFrequency = 98.8e6
const carrierFrequency = 101.7e6
const carrierDeviation = 75e3

const device = await open()
const serial = process.argv[2]
const device = await open(serial)
await device.setFrequency(carrierFrequency + tuneOffset)
await device.setSampleRate(fs)
await device.setAmpEnable(false)
await device.setLnaGain(24)
await device.setVgaGain(8)
await device.setLnaGain(32)
await device.setVgaGain(22)

// Collect audio samples & play back
const speaker = new Speaker({ sampleRate: 48000, channels: 1, bitDepth: 16 })
Expand Down Expand Up @@ -49,14 +50,19 @@ async function main() {
const signal = (x: Complex) => channelFilter(shifter(x))

console.error(`Receiving at ${(carrierFrequency/1e6).toFixed(2)}MHz...`)
process.on('SIGINT', () => device.requestStop())
speaker.on('close', () => { console.error(`Speaker closed`); });
process.on('SIGINT', () => {
speaker.end(); // Close the stream, triggering the Speaker close event
device.requestStop();
} )
await device.receive(array => {
const samples = array.length / 2
for (let n = 0; n < samples; n++)
signal([ array[n * 2 + 0] / 127, array[n * 2 + 1] / 127 ])
})
speaker.destroy()
console.error('\nDone, exiting')
process.exit(0)
}
main()

Expand Down
14 changes: 9 additions & 5 deletions examples/fm_tone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Transmit a tone through FM radio.
*/

import { open } from '..'
import { open } from '../lib'

async function main() {
const fs = 2e6
Expand All @@ -12,24 +12,28 @@ async function main() {
const toneFrequency = 400
const toneAmplitude = .4

const device = await open()
const serial = process.argv[2]
const device = await open(serial)
await device.setFrequency(carrierFrequency)
await device.setSampleRate(fs)
await device.setAmpEnable(false)
await device.setTxVgaGain(30)
await device.setTxVgaGain(47)

const tone = makeToneGeneratorF(fs, toneFrequency, toneAmplitude)
const modulator = makeFrequencyMod(fs, carrierDeviation, carrierAmplitude)
const signal = () => quantize( modulator(tone()) )

console.log(`Transmitting at ${(carrierFrequency/1e6).toFixed(2)}MHz...`)
process.on('SIGINT', () => device.requestStop())
process.on('SIGINT', () => { device.requestStop(); })
let block_count = 0;
await device.transmit(array => {
block_count++;
const samples = array.length / 2
for (let n = 0; n < samples; n++)
array.set(signal(), n * 2)
})
console.log('\nDone, exiting')
console.log(`\nDone after ${block_count} blocks, exiting`);
process.exit(0)
}
main()

Expand Down
19 changes: 19 additions & 0 deletions examples/list_devices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

import { listDevices, UsbBoardId } from '../lib'

async function main() {
let ld = listDevices();
for await (const info of ld) {
console.log(info.device) // Show USB detail
console.log(`Found ${UsbBoardId[info.usbBoardId]} (PID 0x${info.usbBoardId.toString(16).padStart(4, '0')})`)
if (info.serialNumber) {
console.log(`Serial: ${info.serialNumber.replace(/^1+/,'')}`)
}
if (info.boardRev !== undefined) {
console.log(`Board Rev ${info.boardRev}`)
}
}

process.exit(0)
}
main()
8 changes: 8 additions & 0 deletions examples/reception_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { HackrfDevice } from '../lib'

export async function reception_test(rig: HackrfDevice, dut: HackrfDevice, frequency: number): Promise<number> {
console.log(`Testing reception at ${frequency}`)

// REVISIT: Implement receiver test
return 0;
}
25 changes: 25 additions & 0 deletions examples/scan_hackrfs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Continuously scan for HackRF devices, announcing when each appears or disappears
*/

import { DeviceInfo, listDevices, UsbBoardId, visible_hackrfs, scan_hackrfs } from '../lib'

function timeout(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function main() {
for (;;) {
for await (const device_change of scan_hackrfs()) {
if (device_change.added) {
console.log(`Plugged in: ${device_change.added.serialNumber}`)
} else if (device_change.removed) {
console.log(`Unplugged: ${device_change.removed.serialNumber}`)
}
}

await timeout(1000); // Sleep for a second before trying again
}
process.exit(0)
}
main()
94 changes: 94 additions & 0 deletions examples/test_sequence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Testing sequence for the HackRF.
*
* Initially just test the noise floor at a range of frequencies.
* Noise floor is simplified to just the variance in the magnitude of the I/Q vectors.
*/
import { open, HackrfDevice, DeviceInfo, UsbBoardId } from '../lib'

import { reception_test } from "./reception_test"

type Complex = [number, number]

function timeout(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}

/*
* Not really an FFT, not yet. But aspiring to be
*/
class FFT {
protected num_samples: number = 0;
protected sum_mag: number = 0;
protected sum_mag_squared: number = 0;

constructor(_logSize: number) {
// Allocate array of size 2^_logSize
}

accumulate(x: Complex): void {
const mag_squared = x[0]*x[0] + x[1]*x[1]
this.sum_mag_squared += mag_squared;
this.num_samples++;
}

noise_floor(): number
{
return Math.sqrt(this.sum_mag_squared/this.num_samples);
}
}

async function noise_floor_test(dut: HackrfDevice, frequency: number, sample_rate: number): Promise<number> {
console.log(`Measuring noise floor at ${frequency}`)

const num_seconds = 1;

await dut.setFrequency(frequency)
await dut.setSampleRate(sample_rate)
await dut.setAmpEnable(false)
await dut.setLnaGain(32)
await dut.setVgaGain(48)

var fft = new FFT(12);
var num_samples = 0;
await dut.receive((array): undefined | void | false => {
if (num_samples >= num_seconds*sample_rate)
return; // Discard overrun
const samples = array.length / 2
for (let n = 0; n < samples; n++)
{
if (++num_samples == num_seconds*sample_rate) { // Collect 1 second of data
dut.requestStop();
break;
}
const i = array[n * 2 + 0] / 127
const q = array[n * 2 + 1] / 127
fft.accumulate([i, q])
}
})
return fft.noise_floor();
}

export async function run_test_sequence(rig_info: DeviceInfo, dut_info: DeviceInfo)
{
const rig: HackrfDevice = await open(rig_info.serialNumber)
if (!rig)
return `rig ${rig_info.serialNumber} not available`;

const dut: HackrfDevice = await open(dut_info.serialNumber)
if (!dut)
return `HackRF ${dut_info.serialNumber} not available`;

console.log(`Testing ${dut_info.serialNumber} using ${rig_info.serialNumber}:\n`);

const frequencies = [80e6, 600e6, 2.4e9, 3.6e9]
for (let i = 0; i < frequencies.length; i++) {
const frequency = frequencies[i];
var noise_floor = await noise_floor_test(dut, frequency, 1.2e6);
console.log(`Noise floor at ${frequency/1e6} is ${Math.round(noise_floor*1280)/10}`)
}

var frequency = frequencies[0];
var receive_result = await reception_test(rig, dut, frequency);
console.log(`Reception at ${frequency/1e6} returned ${receive_result}`)
}
3 changes: 2 additions & 1 deletion examples/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"strictNullChecks": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
"emitDecoratorMetadata": true,
"outDir": "../dist"
}
}
12 changes: 12 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ export enum VendorRequest {
OPERACAKE_GPIO_TEST = 35,
CPLD_CHECKSUM = 36,
UI_ENABLE = 37,

OPERACAKE_SET_MODE = 38,
OPERACAKE_GET_MODE = 39,
OPERACAKE_SET_DWELL_TIMES = 40,
GET_M0_STATE = 41,
SET_TX_UNDERRUN_LIMIT = 42,
SET_RX_OVERRUN_LIMIT = 43,
GET_CLKIN_STATUS = 44,
BOARD_REV_READ = 45,
SUPPORTED_PLATFORM_READ = 46,
SET_LEDS = 47,
SET_USER_BIAS_T_OPTS = 48,
}

export enum TransceiverMode {
Expand Down
4 changes: 4 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ export {
DeviceInfo, HackrfDevice, listDevices, open,
StreamOptions, defaultStreamOptions,
} from './interface'

export {
visible_hackrfs, scan_hackrfs
} from './scan'
13 changes: 12 additions & 1 deletion lib/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ function detachKernelDrivers(handle: Device) {

export interface DeviceInfo {
device: Device
usbBoardId: number
usbBoardId: number // USB Product ID (PID)
boardRev?: number
serialNumber?: string
}

Expand All @@ -179,6 +180,8 @@ export async function* listDevices() {
device.open(false)
info.serialNumber = await promisify(cb =>
device.getStringDescriptor(iSerialNumber, cb) )() as string
let hackRF = await HackrfDevice.open(device);
info.boardRev = await hackRF.getBoardRev()
} catch (e) {
} finally {
device.close()
Expand Down Expand Up @@ -921,4 +924,12 @@ export class HackrfDevice {
data.subarray(i, i + chunkSize), cb as any) )()
})
}

/**
* @category Device info
*/
async getBoardRev() {
const buf = await this.controlTransferIn(VendorRequest.BOARD_REV_READ, 0, 0, 1)
return checkInLength(buf, 1).readUInt8()
}
}
Loading