// // main.swift // AirSpyHFIQ // // Created by Jacky Jack on 02/12/2024. // import Foundation import ArgumentParser import libairspyhf //set the command line arguments struct CommandLineArgs: ParsableCommand { @Argument var file:String = "" @Option(name:.shortAndLong) var device_idx: Int = 0 @Option(name:.shortAndLong) var samplerate: Int = 192000 @Option(name:.shortAndLong) var gain: Int = 0 @Option(name:.shortAndLong) var frequency: Int = 100000000 @Option(name:.shortAndLong) var nsamples: Int = 1024 @Flag(help:"Version \(software_version)") var version: Bool = false @Flag(name: .shortAndLong) var verbose: Bool = false } let args = CommandLineArgs.parseOrExit() if (args.version) { print("AirSpyHFIQ version \(software_version)") exit(0) } var libersion:airspyhf_lib_version_t = airspyhf_lib_version_t() airspyhf_lib_version(&libersion) print("libairspyhf \(libersion.major_version).\(libersion.minor_version).\(libersion.revision)") let ndev = airspyhf_list_devices(nil, 0) print("Found \(ndev) AirSpyHF devices") if ndev < 0 { exit(0) } //prepare file descriptor if args specify that let currentExePath = Process().currentDirectoryPath var fileDescriptor = FileManager.default var fileArgUrl:URL? var fileHandle:FileHandle? if (args.file != "") { fileArgUrl = URL(fileURLWithFileSystemRepresentation: args.file, isDirectory: false, relativeTo: nil) if (checkIfFileExists(args.file)) { //remove file do { try fileDescriptor.removeItem(atPath: fileArgUrl!.path()) } catch { print("Couldn't delete file that exists \(fileArgUrl!.path())") } } //create file fileDescriptor.createFile(atPath: fileArgUrl!.path(), contents: nil) try fileHandle = FileHandle(forWritingTo: fileArgUrl!) try fileHandle?.seekToEnd() } //will try the first one first var device:AirSpyHF? do { device = try AirSpyHF(idx: 0) } catch { print("Cant attach to device") } let samplerates = device!.getSampleRates() print(samplerates) if (!samplerates.contains(UInt32(args.samplerate))) { print("Unsupported sample rate") exit(0) } //set sample rate var fret:Int32 = -1 fret = device!.setSampleRate(UInt32(args.samplerate)) print("samplerate err=\(fret)") fret = device!.setFreq(UInt32(args.frequency)) print("setfreq err=\(fret)") fret = device!.setHfAgc(1) print("sethfagc err=\(fret)") fret = device!.setHfAgcThreshold(0) print("agcthreshold err=\(fret)") fret = device!.setHfLNA(1) print("hflna err=\(fret)") var sdr_run = true var total_samples:Int32=0 func rf_callback(_ transffer: UnsafeMutablePointer?) -> Int32 { let sample_count = transffer?.pointee.sample_count let rx_bufffer = transffer?.pointee.samples // samples are = float * IQ (I and Q) let bytes_to_write = sample_count! * 4 * 2 total_samples += sample_count! if total_samples > args.nsamples { sdr_run = false return 0 } if let file = fileHandle { let convertedData = Data(bytes: rx_bufffer!, count: Int(bytes_to_write)) do { try file.write(contentsOf: convertedData) } catch { print("Cant dump data to file") } } return 0 } let _ = device!.start(rf_callback) let _ = device!.setFreq(UInt32(args.frequency)) var count=3 var old_total:Int32=1 while ((device!.isStreaming() == 1) && (sdr_run == true)) { //print("Streaming ... total \(total_samples) bytes") //check till the number of samples are written to file or aquired sleep(1) if (old_total == total_samples) { count += 1 } else { count = 0 } old_total = total_samples if (count > 10) { break } } let _ = device!.stop() print("Closing device. Read \(total_samples) samples") device!.close()