diff options
-rw-r--r-- | IQ/IQUtils.swift | 88 | ||||
-rw-r--r-- | LA/Test/MatrixXT/MatrixXT.swift | 12 | ||||
-rw-r--r-- | PrySDR.xcodeproj/project.pbxproj | 151 | ||||
-rw-r--r-- | PrySDR/main.swift | 14 | ||||
-rw-r--r-- | PrySDR_Tests/PrySDR_Tests.swift | 79 | ||||
-rw-r--r-- | Radio/Utils/iqconvert/main.swift | 50 | ||||
-rw-r--r-- | Utils/Double+truncate.swift | 26 | ||||
-rw-r--r-- | Utils/FileReader.swift | 2 | ||||
-rw-r--r-- | Utils/FileWriter.swift | 91 | ||||
-rw-r--r-- | Utils/Version.swift | 2 |
10 files changed, 505 insertions, 10 deletions
diff --git a/IQ/IQUtils.swift b/IQ/IQUtils.swift new file mode 100644 index 0000000..9d1b048 --- /dev/null +++ b/IQ/IQUtils.swift @@ -0,0 +1,88 @@ +// +// IQUtils.swift +// PrySDR +// +// Created by Jacky Jack on 21/01/2025. +// + +import Accelerate + +/// Convert from UInt8 to Float, naive implementation +/// - Parameters: +/// - arr: array of UInt8 +/// - Returns: Return floats -0.996...0.996 +/// +func cnvU8toFloat32(_ arr: [UInt8]) -> [Float32] { + + //let iq = IQ(size: 8, bits: 8, sign: false, complex: true) + + var out: [Float32] = .init(repeating: 0.0, count: arr.count) + + // 0..255 -> -0.996 ... 0.996 + // 0..127 -> -0.996 ... 0.0039 + // 128...255 -> 0.0039 ... 0.996 + for i in 0..<arr.count { + /* + if arr[i] >= 128 { + out[i] = Float(arr[i]-128)/128.0 + } else { // + out[i] = Float(Int(arr[i])-128)/128.0 + }*/ + out[i] = (Float(arr[i])-127.5)/128.0 + } + + return out +} + +/// Convert from UInt8 to Float, naive implementation rtlsdr specfic +/// https://cgit.osmocom.org/gr-osmosdr/tree/lib/rtl/rtl_source_c.cc#n179 +/// - Parameters: +/// - arr: array of UInt8 +/// - Returns: Return floats -0.996...0.996 +/// +func cnvU8toFloat32_rtlsdr(_ arr: [UInt8]) -> [Float32] { + + //let iq = IQ(size: 8, bits: 8, sign: false, complex: true) + + var out: [Float32] = .init(repeating: 0.0, count: arr.count) + + // 0..255 -> -1.0 ... 1.0 + for i in 0..<arr.count { + /* + if arr[i] >= 128 { + out[i] = Float(arr[i]-128)/128.0 + } else { // + out[i] = Float(Int(arr[i])-128)/128.0 + }*/ + out[i] = (Float(arr[i])-127.4)/128.0 + } + + return out +} + + +/// Convert from UInt8 to Float, using Accel framework +/// - Parameters: +/// - arr: array of UInt8 +/// - Returns: Return floats -1.0...1.0 +/// +func cnvU8toFloat32_accel(_ arr: [UInt8]) -> [Float32] { + var out: [Float32] = .init(repeating: 0.0, count: arr.count) + vDSP.convertElements(of: arr, to: &out) + out = vDSP.add(-127.5, out) + out = vDSP.divide(out, 128) + return out +} + +/// Convert from UInt8 to Float, using Accel framework specific to rtlsdr +/// - Parameters: +/// - arr: array of UInt8 +/// - Returns: Return floats -1.0...1.0 +/// +func cnvU8toFloat32_accel_rtlsdr(_ arr: [UInt8]) -> [Float32] { + var out: [Float32] = .init(repeating: 0.0, count: arr.count) + vDSP.convertElements(of: arr, to: &out) + out = vDSP.add(-127.4, out) + out = vDSP.divide(out, 128) + return out +} diff --git a/LA/Test/MatrixXT/MatrixXT.swift b/LA/Test/MatrixXT/MatrixXT.swift index 55661be..a44887a 100644 --- a/LA/Test/MatrixXT/MatrixXT.swift +++ b/LA/Test/MatrixXT/MatrixXT.swift @@ -15,35 +15,35 @@ struct MatrixXT { @Test func matrix_create_with_init() async throws { do { - let _ = try Matrix(row:-1,column: -1,val:0.0) + let _ = try Matrix(rows:-1,columns: -1,val:0.0) Issue.record("Should fail") } catch { //should allway get here, } do { - let _ = try Matrix(row: 0, column: 0, val: 0.0) + let _ = try Matrix(rows: 0, columns: 0, val: 0.0) Issue.record("Should fail") } catch { //should allways get here } do { - let _ = try Matrix(row: 1, column: 1, val: 0.0) + let _ = try Matrix(rows: 1, columns: 1, val: 0.0) } catch { Issue.record("Failed") } do { - let _ = try Matrix(row: 2, column: 2, val: 0.0) + let _ = try Matrix(rows: 2, columns: 2, val: 0.0) } catch { Issue.record("Failed") } do { - let _ = try Matrix(row: 3, column: 3, val: 0.0) + let _ = try Matrix(rows: 3, columns: 3, val: 0.0) } catch { Issue.record("Failed") } do { - let _ = try Matrix(row:16, column: 16, val: 0.0) + let _ = try Matrix(rows:16, columns: 16, val: 0.0) } catch { Issue.record("Failed") } diff --git a/PrySDR.xcodeproj/project.pbxproj b/PrySDR.xcodeproj/project.pbxproj index 0dd3645..ad6651e 100644 --- a/PrySDR.xcodeproj/project.pbxproj +++ b/PrySDR.xcodeproj/project.pbxproj @@ -72,6 +72,8 @@ 8DBA9FA32D37C5CD008ECB92 /* libusb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5A5DDA2CD4B9100096CBD7 /* libusb.a */; }; 8DBA9FA52D37C5D1008ECB92 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 8DBA9FA42D37C5D1008ECB92 /* ArgumentParser */; }; 8DBA9FBE2D3A71A3008ECB92 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 8DBA9FBD2D3A71A3008ECB92 /* ArgumentParser */; }; + 8DBA9FC82D3FA346008ECB92 /* libusb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5A5DDA2CD4B9100096CBD7 /* libusb.a */; }; + 8DBA9FEC2D425776008ECB92 /* Numerics in Frameworks */ = {isa = PBXBuildFile; productRef = 8DBA9FEB2D425776008ECB92 /* Numerics */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -386,6 +388,7 @@ 8DBA9F812D2C979A008ECB92 /* Waterfall_UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Waterfall_UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8DBA9F8B2D2C979A008ECB92 /* Waterfall_UIUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Waterfall_UIUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8DBA9FAE2D3A68B5008ECB92 /* iqconvert */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = iqconvert; sourceTree = BUILT_PRODUCTS_DIR; }; + 8DBA9FD42D3FB893008ECB92 /* PrySDR_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrySDR_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8DD98C412CC592540062D678 /* prysdr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = prysdr; sourceTree = BUILT_PRODUCTS_DIR; }; 8DD98C722CC632040062D678 /* MatrixXT.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MatrixXT.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -474,6 +477,7 @@ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( IQ.swift, + IQUtils.swift, ); target = 8DD98C402CC592540062D678 /* PrySDR */; }; @@ -615,7 +619,6 @@ HW/RtlSdr/libusb/os/threads_posix.c, HW/RtlSdr/libusb/strerror.c, HW/RtlSdr/libusb/sync.c, - HW/RtlSdr/r820/r820.swift, HW/RtlSdr/r820/src/librtlsdr.c, HW/RtlSdr/r820/src/test.swift, HW/RtlSdr/r820/src/tuner_e4k.c, @@ -892,13 +895,43 @@ 8DBA9FC02D3A71D2008ECB92 /* Exceptions for "Utils" folder in "iqconvert" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( + "Double+truncate.swift", FileReader.swift, FileUtils.swift, + FileWriter.swift, PathUtils.swift, Version.swift, ); target = 8DBA9FAD2D3A68B5008ECB92 /* iqconvert */; }; + 8DBA9FCA2D3FA381008ECB92 /* Exceptions for "LA" folder in "PrySDR" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Test/MatrixXT/MatrixXT.swift, + ); + target = 8DD98C402CC592540062D678 /* PrySDR */; + }; + 8DBA9FCF2D3FB1F4008ECB92 /* Exceptions for "IQ" folder in "iqconvert" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + IQUtils.swift, + ); + target = 8DBA9FAD2D3A68B5008ECB92 /* iqconvert */; + }; + 8DBA9FDC2D3FBA2A008ECB92 /* Exceptions for "IQ" folder in "PrySDR_Tests" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + IQUtils.swift, + ); + target = 8DBA9FD32D3FB893008ECB92 /* PrySDR_Tests */; + }; + 8DBA9FE12D425164008ECB92 /* Exceptions for "Utils" folder in "PrySDR_Tests" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + "Double+truncate.swift", + ); + target = 8DBA9FD32D3FB893008ECB92 /* PrySDR_Tests */; + }; 8DD98C7D2CC6320C0062D678 /* Exceptions for "LA" folder in "MatrixXT" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -950,6 +983,11 @@ path = Waterfall_UIUITests; sourceTree = "<group>"; }; + 8DBA9FD52D3FB893008ECB92 /* PrySDR_Tests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = PrySDR_Tests; + sourceTree = "<group>"; + }; 8DD98C432CC592540062D678 /* PrySDR */ = { isa = PBXFileSystemSynchronizedRootGroup; path = PrySDR; @@ -958,6 +996,7 @@ 8DD98C502CC5935B0062D678 /* LA */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( + 8DBA9FCA2D3FA381008ECB92 /* Exceptions for "LA" folder in "PrySDR" target */, 8DD98C7D2CC6320C0062D678 /* Exceptions for "LA" folder in "MatrixXT" target */, ); path = LA; @@ -992,6 +1031,8 @@ isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( 8D5A5CFA2CCD95E90096CBD7 /* Exceptions for "IQ" folder in "PrySDR" target */, + 8DBA9FCF2D3FB1F4008ECB92 /* Exceptions for "IQ" folder in "iqconvert" target */, + 8DBA9FDC2D3FBA2A008ECB92 /* Exceptions for "IQ" folder in "PrySDR_Tests" target */, ); path = IQ; sourceTree = "<group>"; @@ -1046,6 +1087,7 @@ 8D9A38832D19D277009A4186 /* Exceptions for "Utils" folder in "WaterfallFile" target */, 8DBA9F9E2D313168008ECB92 /* Exceptions for "Utils" folder in "Waterfall_UI" target */, 8DBA9FC02D3A71D2008ECB92 /* Exceptions for "Utils" folder in "iqconvert" target */, + 8DBA9FE12D425164008ECB92 /* Exceptions for "Utils" folder in "PrySDR_Tests" target */, ); path = Utils; sourceTree = "<group>"; @@ -1230,10 +1272,19 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 8DBA9FD12D3FB893008ECB92 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DBA9FEC2D425776008ECB92 /* Numerics in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8DD98C3E2CC592540062D678 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8DBA9FC82D3FA346008ECB92 /* libusb.a in Frameworks */, 8D87714A2CD7A6A40082EC54 /* libr820.a in Frameworks */, 8D8771492CD7A69B0082EC54 /* libbladerf.a in Frameworks */, 8D5A5DAE2CD439DA0096CBD7 /* libairspyhf.a in Frameworks */, @@ -1283,6 +1334,7 @@ 8DBA9F702D2C9796008ECB92 /* Waterfall_UI */, 8DBA9F842D2C979A008ECB92 /* Waterfall_UITests */, 8DBA9F8E2D2C979A008ECB92 /* Waterfall_UIUITests */, + 8DBA9FD52D3FB893008ECB92 /* PrySDR_Tests */, 8DD98C422CC592540062D678 /* Products */, 8DD98C642CC599CA0062D678 /* Series */, 8DD98C7F2CC7852E0062D678 /* Images */, @@ -1316,6 +1368,7 @@ 8DBA9F812D2C979A008ECB92 /* Waterfall_UITests.xctest */, 8DBA9F8B2D2C979A008ECB92 /* Waterfall_UIUITests.xctest */, 8DBA9FAE2D3A68B5008ECB92 /* iqconvert */, + 8DBA9FD42D3FB893008ECB92 /* PrySDR_Tests.xctest */, ); name = Products; sourceTree = "<group>"; @@ -1812,6 +1865,29 @@ productReference = 8DBA9FAE2D3A68B5008ECB92 /* iqconvert */; productType = "com.apple.product-type.tool"; }; + 8DBA9FD32D3FB893008ECB92 /* PrySDR_Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8DBA9FD82D3FB893008ECB92 /* Build configuration list for PBXNativeTarget "PrySDR_Tests" */; + buildPhases = ( + 8DBA9FD02D3FB893008ECB92 /* Sources */, + 8DBA9FD12D3FB893008ECB92 /* Frameworks */, + 8DBA9FD22D3FB893008ECB92 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 8DBA9FD52D3FB893008ECB92 /* PrySDR_Tests */, + ); + name = PrySDR_Tests; + packageProductDependencies = ( + 8DBA9FEB2D425776008ECB92 /* Numerics */, + ); + productName = PrySDR_Tests; + productReference = 8DBA9FD42D3FB893008ECB92 /* PrySDR_Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 8DD98C402CC592540062D678 /* PrySDR */ = { isa = PBXNativeTarget; buildConfigurationList = 8DD98C482CC592540062D678 /* Build configuration list for PBXNativeTarget "PrySDR" */; @@ -1947,6 +2023,9 @@ 8DBA9FAD2D3A68B5008ECB92 = { CreatedOnToolsVersion = 16.2; }; + 8DBA9FD32D3FB893008ECB92 = { + CreatedOnToolsVersion = 16.2; + }; 8DD98C402CC592540062D678 = { CreatedOnToolsVersion = 16.0; }; @@ -1966,6 +2045,7 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( 8D0349232CF70E180026DA77 /* XCRemoteSwiftPackageReference "swift-argument-parser" */, + 8DBA9FE22D425453008ECB92 /* XCRemoteSwiftPackageReference "swift-numerics" */, ); preferredProjectObjectVersion = 77; productRefGroup = 8DD98C422CC592540062D678 /* Products */; @@ -1996,6 +2076,7 @@ 8DBA9F802D2C979A008ECB92 /* Waterfall_UITests */, 8DBA9F8A2D2C979A008ECB92 /* Waterfall_UIUITests */, 8DBA9FAD2D3A68B5008ECB92 /* iqconvert */, + 8DBA9FD32D3FB893008ECB92 /* PrySDR_Tests */, ); }; /* End PBXProject section */ @@ -2043,6 +2124,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 8DBA9FD22D3FB893008ECB92 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8DD98C702CC632040062D678 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2200,6 +2288,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 8DBA9FD02D3FB893008ECB92 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8DD98C3D2CC592540062D678 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3017,6 +3112,38 @@ }; name = Release; }; + 8DBA9FD92D3FB893008ECB92 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "RadioTeam.PrySDR-Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 8DBA9FDA2D3FB893008ECB92 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "RadioTeam.PrySDR-Tests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 8DD98C462CC592540062D678 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3394,6 +3521,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 8DBA9FD82D3FB893008ECB92 /* Build configuration list for PBXNativeTarget "PrySDR_Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DBA9FD92D3FB893008ECB92 /* Debug */, + 8DBA9FDA2D3FB893008ECB92 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 8DD98C3C2CC592540062D678 /* Build configuration list for PBXProject "PrySDR" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3432,6 +3568,14 @@ minimumVersion = 1.5.0; }; }; + 8DBA9FE22D425453008ECB92 /* XCRemoteSwiftPackageReference "swift-numerics" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-numerics.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.2; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -3475,6 +3619,11 @@ package = 8D0349232CF70E180026DA77 /* XCRemoteSwiftPackageReference "swift-argument-parser" */; productName = ArgumentParser; }; + 8DBA9FEB2D425776008ECB92 /* Numerics */ = { + isa = XCSwiftPackageProductDependency; + package = 8DBA9FE22D425453008ECB92 /* XCRemoteSwiftPackageReference "swift-numerics" */; + productName = Numerics; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 8DD98C392CC592540062D678 /* Project object */; diff --git a/PrySDR/main.swift b/PrySDR/main.swift index 74f3f67..c3b70d6 100644 --- a/PrySDR/main.swift +++ b/PrySDR/main.swift @@ -15,3 +15,17 @@ import libbladerf print("PrySDR") print("We will start soon on exploring things \(software_version)") +print("Detect RtlSdr") +let countRtlSdr = getDeviceCount() +print("Found \(countRtlSdr) r820 devices") + +print("Detect AirSpy") + +print("Detect AirSpyHF") +let countAirSpyHF = airspyhf_list_devices(nil, 0) +print("Found \(countAirSpyHF) AirSpyHF devices") +if countAirSpyHF < 0 { + exit(0) +} + +print("Detect BladeRF") diff --git a/PrySDR_Tests/PrySDR_Tests.swift b/PrySDR_Tests/PrySDR_Tests.swift new file mode 100644 index 0000000..70b9828 --- /dev/null +++ b/PrySDR_Tests/PrySDR_Tests.swift @@ -0,0 +1,79 @@ +// +// PrySDR_Tests.swift +// PrySDR_Tests +// +// Created by Jacky Jack on 21/01/2025. +// + +import Testing +import Numerics + +struct IQConverter_Tests { + + @Test func convertU8arrayToFloat32_accel_size1() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + let arr1:[UInt8] = [0] + let res1 = cnvU8toFloat32_accel(arr1) + #expect(res1[0].truncate(places: 3) == -0.997) + //#expect(res1[0].isApproximatelyEqual(to: -0.997, absoluteTolerance: 0.001)) + + + let arr2:[UInt8] = [255] + let res2 = cnvU8toFloat32_accel(arr2) + #expect(res2[0].truncate(places: 3) == 0.996) + + let arr3:[UInt8] = [127] + let res3 = cnvU8toFloat32_accel(arr3) + #expect(res3[0].truncate(places: 3) == -0.004) + + let arr4:[UInt8] = [128] + let res4 = cnvU8toFloat32_accel(arr4) + #expect(res4[0].truncate(places: 3) == 0.003) + } + /* + @Test func cnvU8arrayToFloat32_size16() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + #expect(false) + } + */ + /* + @Test func cnvU8arrayToFloat32_size1024() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + #expect(false) + } + */ + + @Test func cnvU8arrayToFloat32_naive_size1() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + let arr1:[UInt8] = [0] + let res1 = cnvU8toFloat32(arr1) + //minus truncates to "up" + #expect(res1[0].truncate(places: 3) == -0.997) + + let arr2:[UInt8] = [255] + let res2 = cnvU8toFloat32(arr2) + #expect(res2[0].truncate(places: 3) == 0.996) + + let arr3:[UInt8] = [127] + let res3 = cnvU8toFloat32(arr3) + //minus truncates to "up" + #expect(res3[0].truncate(places: 3) == -0.004) + + let arr4:[UInt8] = [128] + let res4 = cnvU8toFloat32(arr4) + #expect(res4[0].truncate(places: 3) == 0.003) + } + /* + @Test func cnvU8arrayToFloat32_naive_size16() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + #expect(false) + } + */ + + /* + @Test func cnvU8arrayToFloat32_naive_size1024() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + #expect(false) + } + */ +} diff --git a/Radio/Utils/iqconvert/main.swift b/Radio/Utils/iqconvert/main.swift index f619529..49db280 100644 --- a/Radio/Utils/iqconvert/main.swift +++ b/Radio/Utils/iqconvert/main.swift @@ -35,7 +35,7 @@ if args.listFormats { print(" sc16q11: BladeRF [not supported]") print(" fc32r: AirSpy [not supported]") //float32 real 1 * 32bit per sampl print("Supported output:") - print(" fc32: inspectrum, sdr++ [not supported]") + print(" fc32: inspectrum, sdr++ ") print(" wav: SDR# [not suppoprted]") } @@ -84,8 +84,54 @@ if let dotIndex = args.outputFile.lastIndex(of: ".") { print("Input file format:\(inputFormat) output file format:\(outputFormat)") +//read from file +//if let data_u8:[UInt8] = fileReader.readAll() { +var data_u8:[UInt8] = [] +do { + data_u8 = try fileReader.readAll() +} catch { + print("Reading file: \(error)") + exit(1) +} -fileReader.close() +//convert to output format +var outdata_f32:[Float32] = [] +if data_u8.count > 0 { + outdata_f32 = cnvU8toFloat32(data_u8) +} else { + print("No data in buffer") +} +// write to file +let fileWrite = FileWriter() +var file_ok = false +if (checkIfFileExists(args.outputFile)) { + do { + print("try to delet file for write") + try fileWrite.deleteForWrite(filename: args.outputFile) + } catch { + print("cant delete file \(error)") + exit(1) + } +} else { + do { + print("try to creating file") + try fileWrite.createForWrite(filename: args.outputFile) + } catch { + print("cant create file \(error)") + exit(1) + } +} + +do { + try fileWrite.writeAll(outdata_f32) +} catch { + print("Cant write to file \(error)") + exit(1) +} + + +fileReader.close() +fileWrite.close() diff --git a/Utils/Double+truncate.swift b/Utils/Double+truncate.swift new file mode 100644 index 0000000..b86d51d --- /dev/null +++ b/Utils/Double+truncate.swift @@ -0,0 +1,26 @@ +// +// Double+truncate.swift +// PrySDR +// +// Created by Jacky Jack on 23/01/2025. +// + +import Foundation + +extension Double { + func truncate(places : Int)-> Double { + return Double(floor(Foundation.pow(10.0, Double(places)) * self)/Foundation.pow(10.0, Double(places))) + } +} + +extension Float32 { + func truncate(places : Int)-> Float32 { + return Float32(floor(Foundation.pow(10.0, Float32(places)) * self)/Foundation.pow(10.0, Float32(places))) + } +} + +extension Float32 { + var bytes: [UInt8] { + withUnsafeBytes(of: self, Array.init) + } +} diff --git a/Utils/FileReader.swift b/Utils/FileReader.swift index d4be92d..1f9c561 100644 --- a/Utils/FileReader.swift +++ b/Utils/FileReader.swift @@ -143,6 +143,8 @@ class FileReader { } } + + func close() { fileHandle?.closeFile() } diff --git a/Utils/FileWriter.swift b/Utils/FileWriter.swift new file mode 100644 index 0000000..6330342 --- /dev/null +++ b/Utils/FileWriter.swift @@ -0,0 +1,91 @@ +// +// FileWriter.swift +// PrySDR +// +// Created by Jacky Jack on 21/01/2025. +// + +import Foundation + +enum FileWriterError: Error { + case noFile + case writeError + case createError +} + +class FileWriter { + var filepath: String? + let filemgr = FileManager.default + var fileHandle: FileHandle? + + init() { + + } + + func openForRead(filename: String) throws { + let dir = getCurrentExecutableDir() + self.filepath = dir+"/"+filename + print(dir+"/"+filename) + if !filemgr.fileExists(atPath: filename) { + print("Cant find file \(filename)") + throw FileReaderError.noFile + } + + fileHandle = FileHandle(forReadingAtPath: dir+"/"+filename) + if (fileHandle == nil) { + print("FileHandler failed") + } + } + + func createForWrite(filename: String) throws { + let dir = getCurrentExecutableDir() + + self.filepath = dir+"/"+filename + + if !filemgr.fileExists(atPath: filename) { + filemgr.createFile(atPath: dir+"/"+filename, contents: nil) + } + fileHandle = FileHandle(forUpdatingAtPath: dir+"/"+filename) + if (fileHandle == nil) { + print("FileHandler failed") + throw FileWriterError.createError + } + } + + func deleteForWrite(filename: String) throws { + let dir = getCurrentExecutableDir() + + self.filepath = dir+"/"+filename + + if filemgr.fileExists(atPath: filename) { + do { + try filemgr.removeItem(atPath: filename) + filemgr.createFile(atPath: dir+"/"+filename, contents: nil) + } catch { + print("couldnt delete file to write: \(error)") + } + } + fileHandle = FileHandle(forWritingAtPath: dir+"/"+filename) + if (fileHandle == nil) { + print("FileHandler failed") + throw FileWriterError.createError + } + } + + func writeAll(_ arr: [Float32]) throws { + //convert float to data + let databuf = arr.withUnsafeBytes { Data($0) } + do { + print("try to write \(databuf.count) bytes") + try fileHandle?.write(contentsOf: databuf) + } catch { + print("Cant write to file \(self.filepath) : \(error)") + throw FileWriterError.writeError + } + } + + func close() { + fileHandle?.closeFile() + } + +} diff --git a/Utils/Version.swift b/Utils/Version.swift index e9283d3..accda99 100644 --- a/Utils/Version.swift +++ b/Utils/Version.swift @@ -5,4 +5,4 @@ // Created by Jacky Jack on 02/12/2024. // -public let software_version = "2025.1-1" +public let software_version = "2025.2-1" |