// // main.swift // AirSpyIQ // // Created by Jacky Jack on 02/12/2024. // import Foundation import ArgumentParser import libairspy //set the command line arguments struct CommandLineArgs: ParsableCommand { @Argument var file:String = "" @Option() var serial: UInt64 = 0 @Option(name:.shortAndLong) var samplerate: Int = 3000000 @Option(name:.shortAndLong) var gain: Int = 0 @Option(name:.shortAndLong) var frequency: Int = 100000000 @Option(name:.shortAndLong) var nsamples: Int = 65536 @Flag(help:"Version \(software_version)") var version: Bool = false @Flag(name: .shortAndLong) var verbose: Bool = false } let args = CommandLineArgs.parseOrExit() if (args.version) { print("AirSpyIQ version \(software_version)") exit(0) } var libersion:airspy_lib_version_t = airspy_lib_version_t() airspy_lib_version(&libersion) print("libairspy \(libersion.major_version).\(libersion.minor_version).\(libersion.revision)") //detect number of devices var dev:UnsafeMutablePointer? = .allocate(capacity: 1) var ret:Int32 ret = airspy_init() if (ret != AIRSPY_SUCCESS.rawValue) { print("Couldnt init library") exit(1) } //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() } var device:AirSpy? do { device = try AirSpy() } catch { print("Cant attach to device") } let sample_rates = device?.getSampleRates() let _ = device?.setSampleRate(sample_rates![0]) print("sample rate \(sample_rates![0])") var sdr_run = true var total_samples:Int32=0 func rf_callback(_ transffer: UnsafeMutablePointer?) -> Int32 { let t = transffer!.pointee let rx_bufffer = t.samples let sample_count = t.sample_count //default value if (t.sample_type != AIRSPY_SAMPLE_FLOAT32_IQ) { print("Unsupported sample type to write") } let bytes_to_write = sample_count*2*4 //IQ*float 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") } } print("Got \(sample_count) samples") return 0 } let _ = device?.VGAGain(5) let _ = device?.mixerGain(5) let _ = device?.lnaGain(1) let _ = device?.startRx(rf_callback) let _ = device?.setFrequency(UInt32(args.frequency)) sleep(1) var count = 10 while ((device?.isStreaming() != AIRSPY_TRUE.rawValue) && (count>0) && (sdr_run)) { print("Streaming") sleep(1) count -= 1 } let _ = device?.stopRx() let _ = device?.close() airspy_exit()