diff options
-rw-r--r-- | PrySDR.xcodeproj/project.pbxproj | 144 | ||||
-rw-r--r-- | Radio/HW/RtlSdr/r820/r820.swift | 9 | ||||
-rw-r--r-- | Radio/Utils/RtlSdrIQ/main.swift | 77 | ||||
-rw-r--r-- | Utils/FileUtils.swift | 18 | ||||
-rw-r--r-- | Utils/PathUtils.swift | 13 |
5 files changed, 251 insertions, 10 deletions
diff --git a/PrySDR.xcodeproj/project.pbxproj b/PrySDR.xcodeproj/project.pbxproj index 634f2b7..9e31fe4 100644 --- a/PrySDR.xcodeproj/project.pbxproj +++ b/PrySDR.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ buildPhases = ( ); dependencies = ( + 8D4068282CF88CCF0064C96D /* PBXTargetDependency */, 8D876E8D2CD4DBE80082EC54 /* PBXTargetDependency */, 8D876E8B2CD4DBE50082EC54 /* PBXTargetDependency */, 8D876E892CD4DBE20082EC54 /* PBXTargetDependency */, @@ -26,6 +27,9 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 8D0349252CF70E220026DA77 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 8D0349242CF70E220026DA77 /* ArgumentParser */; }; + 8D4068242CF8619B0064C96D /* libr820.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5A5DB42CD4B70D0096CBD7 /* libr820.a */; }; + 8D4068252CF8619F0064C96D /* libusb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5A5DDA2CD4B9100096CBD7 /* libusb.a */; }; 8D5A5DAD2CD439D70096CBD7 /* libairspy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5A5D752CD436BC0096CBD7 /* libairspy.a */; }; 8D5A5DAE2CD439DA0096CBD7 /* libairspyhf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5A5D9F2CD439360096CBD7 /* libairspyhf.a */; }; 8D5A5DF02CD4B97E0096CBD7 /* libusb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5A5DDA2CD4B9100096CBD7 /* libusb.a */; }; @@ -46,6 +50,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 8D4068272CF88CCF0064C96D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8DD98C392CC592540062D678 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D0349172CF70DCD0026DA77; + remoteInfo = RtlSdrIQ; + }; 8D5A5E1E2CD4C18D0096CBD7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8DD98C392CC592540062D678 /* Project object */; @@ -126,6 +137,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 8D0349162CF70DCD0026DA77 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 8D5A5E062CD4BAF10096CBD7 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -174,6 +194,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8D0349182CF70DCD0026DA77 /* RtlSdrIQ */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = RtlSdrIQ; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5A5D752CD436BC0096CBD7 /* libairspy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libairspy.a; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5A5D9F2CD439360096CBD7 /* libairspyhf.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libairspyhf.a; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5A5DB42CD4B70D0096CBD7 /* libr820.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libr820.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -191,6 +212,22 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 8D0349222CF70DD20026DA77 /* Exceptions for "Radio" folder in "RtlSdrIQ" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + HW/RtlSdr/r820/r820.swift, + Utils/RtlSdrIQ/main.swift, + ); + target = 8D0349172CF70DCD0026DA77 /* RtlSdrIQ */; + }; + 8D4068302CF9BF360064C96D /* Exceptions for "Utils" folder in "RtlSdrIQ" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + FileUtils.swift, + PathUtils.swift, + ); + target = 8D0349172CF70DCD0026DA77 /* RtlSdrIQ */; + }; 8D5A5CFA2CCD95E90096CBD7 /* Exceptions for "IQ" folder in "PrySDR" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -344,6 +381,7 @@ HW/RtlSdr/r820/src/tuner_fc0013.c, HW/RtlSdr/r820/src/tuner_fc2580.c, HW/RtlSdr/r820/src/tuner_r82xx.c, + Utils/RtlSdrIQ/main.swift, Utils/TestAirSpy/main.swift, Utils/TestAirSpyHF/main.swift, Utils/TestBladeRF/main.swift, @@ -583,6 +621,7 @@ 8D876E812CD4DB950082EC54 /* Exceptions for "Radio" folder in "TestAirSpyHF" target */, 8D8770072CD4F1680082EC54 /* Exceptions for "Radio" folder in "TestBladeRF" target */, 8D8770A22CD6B4D00082EC54 /* Exceptions for "Radio" folder in "libbladerf" target */, + 8D0349222CF70DD20026DA77 /* Exceptions for "Radio" folder in "RtlSdrIQ" target */, ); path = Radio; sourceTree = "<group>"; @@ -632,12 +671,25 @@ }; 8DD98C882CC788620062D678 /* Utils */ = { isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 8D4068302CF9BF360064C96D /* Exceptions for "Utils" folder in "RtlSdrIQ" target */, + ); path = Utils; sourceTree = "<group>"; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ + 8D0349152CF70DCD0026DA77 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D4068252CF8619F0064C96D /* libusb.a in Frameworks */, + 8D4068242CF8619B0064C96D /* libr820.a in Frameworks */, + 8D0349252CF70E220026DA77 /* ArgumentParser in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8D5A5D732CD436BC0096CBD7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -781,6 +833,7 @@ 8D876E772CD4DB8F0082EC54 /* TestAirSpyHF */, 8D876FFD2CD4F1620082EC54 /* TestBladeRF */, 8D87709D2CD6B4BB0082EC54 /* libbladerf.a */, + 8D0349182CF70DCD0026DA77 /* RtlSdrIQ */, ); name = Products; sourceTree = "<group>"; @@ -840,6 +893,26 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 8D0349172CF70DCD0026DA77 /* RtlSdrIQ */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D03491E2CF70DCD0026DA77 /* Build configuration list for PBXNativeTarget "RtlSdrIQ" */; + buildPhases = ( + 8D0349142CF70DCD0026DA77 /* Sources */, + 8D0349152CF70DCD0026DA77 /* Frameworks */, + 8D0349162CF70DCD0026DA77 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RtlSdrIQ; + packageProductDependencies = ( + 8D0349242CF70E220026DA77 /* ArgumentParser */, + ); + productName = RtlSdrIQ; + productReference = 8D0349182CF70DCD0026DA77 /* RtlSdrIQ */; + productType = "com.apple.product-type.tool"; + }; 8D5A5D742CD436BC0096CBD7 /* libairspy */ = { isa = PBXNativeTarget; buildConfigurationList = 8D5A5D762CD436BC0096CBD7 /* Build configuration list for PBXNativeTarget "libairspy" */; @@ -1081,6 +1154,9 @@ LastSwiftUpdateCheck = 1610; LastUpgradeCheck = 1610; TargetAttributes = { + 8D0349172CF70DCD0026DA77 = { + CreatedOnToolsVersion = 16.1; + }; 8D5A5D742CD436BC0096CBD7 = { CreatedOnToolsVersion = 16.0; LastSwiftMigration = 1610; @@ -1131,6 +1207,9 @@ ); mainGroup = 8DD98C382CC592540062D678; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 8D0349232CF70E180026DA77 /* XCRemoteSwiftPackageReference "swift-argument-parser" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 8DD98C422CC592540062D678 /* Products */; projectDirPath = ""; @@ -1148,6 +1227,7 @@ 8D876E822CD4DBD80082EC54 /* BuildAll */, 8D876FFC2CD4F1620082EC54 /* TestBladeRF */, 8D87709C2CD6B4BB0082EC54 /* libbladerf */, + 8D0349172CF70DCD0026DA77 /* RtlSdrIQ */, ); }; /* End PBXProject section */ @@ -1163,6 +1243,13 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 8D0349142CF70DCD0026DA77 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8D5A5D722CD436BC0096CBD7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1243,6 +1330,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 8D4068282CF88CCF0064C96D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D0349172CF70DCD0026DA77 /* RtlSdrIQ */; + targetProxy = 8D4068272CF88CCF0064C96D /* PBXContainerItemProxy */; + }; 8D5A5E1F2CD4C18D0096CBD7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8D5A5DB32CD4B70D0096CBD7 /* libr820 */; @@ -1301,6 +1393,30 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 8D03491C2CF70DCD0026DA77 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + ENABLE_HARDENED_RUNTIME = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 8D03491D2CF70DCD0026DA77 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + ENABLE_HARDENED_RUNTIME = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 8D5A5D772CD436BC0096CBD7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1717,6 +1833,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 8D03491E2CF70DCD0026DA77 /* Build configuration list for PBXNativeTarget "RtlSdrIQ" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D03491C2CF70DCD0026DA77 /* Debug */, + 8D03491D2CF70DCD0026DA77 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 8D5A5D762CD436BC0096CBD7 /* Build configuration list for PBXNativeTarget "libairspy" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1835,6 +1960,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D0349232CF70E180026DA77 /* XCRemoteSwiftPackageReference "swift-argument-parser" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-argument-parser.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.5.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D0349242CF70E220026DA77 /* ArgumentParser */ = { + isa = XCSwiftPackageProductDependency; + package = 8D0349232CF70E180026DA77 /* XCRemoteSwiftPackageReference "swift-argument-parser" */; + productName = ArgumentParser; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 8DD98C392CC592540062D678 /* Project object */; } diff --git a/Radio/HW/RtlSdr/r820/r820.swift b/Radio/HW/RtlSdr/r820/r820.swift index 3f6d4a9..4c8859f 100644 --- a/Radio/HW/RtlSdr/r820/r820.swift +++ b/Radio/HW/RtlSdr/r820/r820.swift @@ -189,8 +189,13 @@ public class R820Tuner { /// - len: input buffer length /// - n_read: number of bytes received /// - Returns: -1 if there is no device, 0 on success or one of LIBUSB error - /// LIBUSB_ERROR_TIMEOUT, LIBUSB_ERROR_PIPE,LIBUSB_ERROR_OVERFLOW - /// LIBUSB_ERROR_NO_DEVICE, LIBUSB_ERROR_BUSY LIBUSB_ERROR_INVALID_PARAM + /// on success, the number of bytes actually transferred + /// LIBUSB_ERROR_TIMEOUT if the transfer timed out + /// LIBUSB_ERROR_PIPE if the control request was not supported by the device + /// LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + /// LIBUSB_ERROR_BUSY if called from event handling context + /// LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than the operating system and/or hardware can support (see Transfer length limitations) + /// another LIBUSB_ERROR code on other failures /// func readSync(buf:UnsafeMutableRawPointer?,len: Int32,n_read:UnsafeMutablePointer<Int32>?) -> Int32 { return rtlsdr_read_sync(dev, buf, len, n_read); diff --git a/Radio/Utils/RtlSdrIQ/main.swift b/Radio/Utils/RtlSdrIQ/main.swift index 20573a9..c3d5732 100644 --- a/Radio/Utils/RtlSdrIQ/main.swift +++ b/Radio/Utils/RtlSdrIQ/main.swift @@ -9,13 +9,15 @@ import Foundation import libr820 import ArgumentParser +let NUM_SAMPLES=1024 + //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 = 2048000 @Option(name:.shortAndLong) var gain: Int = 0 - @Option(name: .shortAndLong) var frequency: Int = 100000000 + @Option(name:.shortAndLong) var frequency: Int = 100000000 @Option(name:.shortAndLong) var nsamples: Int = 1024 @Flag(name: .shortAndLong) var verbose: Bool = false @Flag(name: .shortAndLong) var async: Bool = false @@ -40,6 +42,7 @@ if (args.device_idx < 0) || (args.device_idx > count-1) { let device = R820Tuner() device.open(index: UInt32(args.device_idx)) +//prepare dongle to receive some data let _ = device.setSampleRate(samplerate: UInt32(args.samplerate)) let _ = device.setCenterFreq(freq: UInt32(args.frequency)) if args.gain == 0 { @@ -48,15 +51,73 @@ if args.gain == 0 { let _ = device.setTunerGain(gain: Int32(args.gain)) } let _ = device.resetBuffer() -let buf_ptr = UnsafeMutableRawPointer.allocate(byteCount: args.nsamples, alignment: 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() +} + +//prepare loop buffers to process data +let buf_ptr = UnsafeMutableRawPointer.allocate(byteCount: NUM_SAMPLES, alignment: 1) var nbytes:Int32 = 0 -let r = device.readSync(buf: buf_ptr, len: Int32(args.nsamples), n_read: &nbytes) -print("Reading samples return code (\(r)) read \(nbytes) bytes\n") -for i in 0..<args.nsamples { - let offsetByte = buf_ptr + i - print("\(String(format:"%02hhX ",offsetByte.load(as: UInt8.self)))",terminator: "") +var total_bytes:Int32 = 0 +if (args.async == false) { + while (total_bytes < args.nsamples) { + let ret = device.readSync(buf: buf_ptr, len: Int32(NUM_SAMPLES), n_read: &nbytes) + if ret<0 { + print("data read sync returned <0 = \(ret)") + break; + } + print("Reading samples read \(nbytes) bytes") + total_bytes += nbytes + + let dataU8 = buf_ptr.bindMemory(to: UInt8.self, capacity: NUM_SAMPLES) + let bufferU8 = UnsafeBufferPointer(start: dataU8, count: NUM_SAMPLES) + let convertedData = Data(bufferU8) + /*for i in 0..<Int(nbytes) { + let offsetByte = buf_ptr + i + print("\(String(format:"%02hhX ",offsetByte.load(as: UInt8.self)))",terminator: "") + }*/ + if (args.verbose) { + for i in 0..<bufferU8.count { + print("\(String(format: "%02hhX", bufferU8[i]))", terminator: "") + } + } + + //dump data to file + + if let file = fileHandle { + //print(convertedData) + do { + //try convertedData.write(to: fileUrl) + try file.write(contentsOf: convertedData) + } catch { + print("Cant dump data to file") + } + } + } +} else { + print("ASYNC not implemented") } -device.close() +//clean after use all structures +device.close() buf_ptr.deallocate() diff --git a/Utils/FileUtils.swift b/Utils/FileUtils.swift new file mode 100644 index 0000000..58e7124 --- /dev/null +++ b/Utils/FileUtils.swift @@ -0,0 +1,18 @@ +// +// FileUtils.swift +// PrySDR +// +// Created by Jacky Jack on 29/11/2024. +// + +import Foundation + +func checkIfFileExists(_ fname: String) -> Bool { + let fm = FileManager.default + if fm.fileExists(atPath: fname) { + return true + } + return false +} + + diff --git a/Utils/PathUtils.swift b/Utils/PathUtils.swift new file mode 100644 index 0000000..a82261a --- /dev/null +++ b/Utils/PathUtils.swift @@ -0,0 +1,13 @@ +// +// PathUtils.swift +// PrySDR +// +// Created by Jacky Jack on 29/11/2024. +// + +import Foundation + +//get current run directory +func getCurrentExecutableDir() -> String { + return Process().currentDirectoryPath +} |