diff options
-rw-r--r-- | PrySDR.xcodeproj/project.pbxproj | 527 | ||||
-rw-r--r-- | Radio/Utils/WaterfallFile/NaiveFFT.swift | 7 | ||||
-rw-r--r-- | Radio/Utils/WaterfallFile/SimpleImage.swift | 116 | ||||
-rw-r--r-- | Radio/Utils/WaterfallFile/main.swift | 113 | ||||
-rw-r--r-- | WaterfallFile_UI/Assets.xcassets/AccentColor.colorset/Contents.json | 11 | ||||
-rw-r--r-- | WaterfallFile_UI/Assets.xcassets/AppIcon.appiconset/Contents.json | 58 | ||||
-rw-r--r-- | WaterfallFile_UI/Assets.xcassets/Contents.json | 6 | ||||
-rw-r--r-- | WaterfallFile_UI/ContentView.swift | 94 | ||||
-rw-r--r-- | WaterfallFile_UI/FileSpectrum.swift | 64 | ||||
-rw-r--r-- | WaterfallFile_UI/Item.swift | 18 | ||||
-rw-r--r-- | WaterfallFile_UI/Preview Content/Preview Assets.xcassets/Contents.json | 6 | ||||
-rw-r--r-- | WaterfallFile_UI/WaterfallFile_UI.entitlements | 10 | ||||
-rw-r--r-- | WaterfallFile_UI/WaterfallFile_UIApp.swift | 45 | ||||
-rw-r--r-- | WaterfallFile_UITests/WaterfallFile_UITests.swift | 17 | ||||
-rw-r--r-- | WaterfallFile_UIUITests/WaterfallFile_UIUITests.swift | 43 | ||||
-rw-r--r-- | WaterfallFile_UIUITests/WaterfallFile_UIUITestsLaunchTests.swift | 33 |
16 files changed, 1167 insertions, 1 deletions
diff --git a/PrySDR.xcodeproj/project.pbxproj b/PrySDR.xcodeproj/project.pbxproj index 233a68b..5d627d2 100644 --- a/PrySDR.xcodeproj/project.pbxproj +++ b/PrySDR.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ buildPhases = ( ); dependencies = ( + 8D9A38892D19D4E1009A4186 /* PBXTargetDependency */, + 8D9A38872D19D4DC009A4186 /* PBXTargetDependency */, 8D4068682CFDC0940064C96D /* PBXTargetDependency */, 8D4068662CFDC0900064C96D /* PBXTargetDependency */, 8D4068642CFDC08D0064C96D /* PBXTargetDependency */, @@ -65,6 +67,7 @@ 8D9A334F2D0B0DD6009A4186 /* libusb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5A5DDA2CD4B9100096CBD7 /* libusb.a */; }; 8D9A33502D0B0DD8009A4186 /* libbladerf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D87709D2CD6B4BB0082EC54 /* libbladerf.a */; }; 8D9A33522D0B0DE5009A4186 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 8D9A33512D0B0DE5009A4186 /* ArgumentParser */; }; + 8D9A38852D19D427009A4186 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 8D9A38842D19D427009A4186 /* ArgumentParser */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -208,6 +211,34 @@ remoteGlobalIDString = 8D87709C2CD6B4BB0082EC54; remoteInfo = libbladerf; }; + 8D9A38592D194A41009A4186 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8DD98C392CC592540062D678 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D9A38452D194A3D009A4186; + remoteInfo = WaterfallFile_UI; + }; + 8D9A38632D194A41009A4186 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8DD98C392CC592540062D678 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D9A38452D194A3D009A4186; + remoteInfo = WaterfallFile_UI; + }; + 8D9A38862D19D4DC009A4186 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8DD98C392CC592540062D678 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D9A33432D0B0D8D009A4186; + remoteInfo = BladeRFIQ; + }; + 8D9A38882D19D4E1009A4186 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8DD98C392CC592540062D678 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D9A38762D196510009A4186; + remoteInfo = WaterfallFile; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -283,6 +314,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 8D9A38752D196510009A4186 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 8DD98C3F2CC592540062D678 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -311,6 +351,10 @@ 8D876FFD2CD4F1620082EC54 /* test_bladerf */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test_bladerf; sourceTree = BUILT_PRODUCTS_DIR; }; 8D87709D2CD6B4BB0082EC54 /* libbladerf.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbladerf.a; sourceTree = BUILT_PRODUCTS_DIR; }; 8D9A33442D0B0D8D009A4186 /* bladerf_iq */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = bladerf_iq; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D9A38462D194A3D009A4186 /* WaterfallFile_UI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WaterfallFile_UI.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D9A38582D194A41009A4186 /* WaterfallFile_UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WaterfallFile_UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D9A38622D194A41009A4186 /* WaterfallFile_UIUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WaterfallFile_UIUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D9A38772D196510009A4186 /* waterfall_file */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = waterfall_file; 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 */ @@ -556,6 +600,9 @@ Utils/TestAirSpyHF/main.swift, Utils/TestBladeRF/main.swift, Utils/TestRtlSdr/main.swift, + Utils/WaterfallFile/main.swift, + Utils/WaterfallFile/NaiveFFT.swift, + Utils/WaterfallFile/SimpleImage.swift, ); target = 8DD98C402CC592540062D678 /* PrySDR */; }; @@ -753,6 +800,30 @@ ); target = 8D9A33432D0B0D8D009A4186 /* BladeRFIQ */; }; + 8D9A38812D196520009A4186 /* Exceptions for "Radio" folder in "WaterfallFile" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Utils/WaterfallFile/main.swift, + Utils/WaterfallFile/NaiveFFT.swift, + Utils/WaterfallFile/SimpleImage.swift, + ); + target = 8D9A38762D196510009A4186 /* WaterfallFile */; + }; + 8D9A38832D19D277009A4186 /* Exceptions for "Utils" folder in "WaterfallFile" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + PathUtils.swift, + ); + target = 8D9A38762D196510009A4186 /* WaterfallFile */; + }; + 8DBA9F602D2928DE008ECB92 /* Exceptions for "Radio" folder in "WaterfallFile_UI" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Utils/WaterfallFile/NaiveFFT.swift, + Utils/WaterfallFile/SimpleImage.swift, + ); + target = 8D9A38452D194A3D009A4186 /* WaterfallFile_UI */; + }; 8DD98C7C2CC6320C0062D678 /* Exceptions for "LA" folder in "PrySDR" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -781,6 +852,21 @@ path = Gen; sourceTree = "<group>"; }; + 8D9A38472D194A3D009A4186 /* WaterfallFile_UI */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = WaterfallFile_UI; + sourceTree = "<group>"; + }; + 8D9A385B2D194A41009A4186 /* WaterfallFile_UITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = WaterfallFile_UITests; + sourceTree = "<group>"; + }; + 8D9A38652D194A41009A4186 /* WaterfallFile_UIUITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = WaterfallFile_UIUITests; + sourceTree = "<group>"; + }; 8DD98C432CC592540062D678 /* PrySDR */ = { isa = PBXFileSystemSynchronizedRootGroup; path = PrySDR; @@ -812,6 +898,8 @@ 8D4068402CFDA6A30064C96D /* Exceptions for "Radio" folder in "AirSpyHFIQ" target */, 8D40684F2CFDA6BD0064C96D /* Exceptions for "Radio" folder in "AirSpyIQ" target */, 8D9A334E2D0B0D92009A4186 /* Exceptions for "Radio" folder in "BladeRFIQ" target */, + 8DBA9F602D2928DE008ECB92 /* Exceptions for "Radio" folder in "WaterfallFile_UI" target */, + 8D9A38812D196520009A4186 /* Exceptions for "Radio" folder in "WaterfallFile" target */, ); path = Radio; sourceTree = "<group>"; @@ -870,6 +958,7 @@ 8D40686D2CFDE4C10064C96D /* Exceptions for "Utils" folder in "AirSpyHFIQ" target */, 8D406AB52CFF0D1F0064C96D /* Exceptions for "Utils" folder in "AirSpyIQ" target */, 8D9A37F12D180D4C009A4186 /* Exceptions for "Utils" folder in "BladeRFIQ" target */, + 8D9A38832D19D277009A4186 /* Exceptions for "Utils" folder in "WaterfallFile" target */, ); path = Utils; sourceTree = "<group>"; @@ -993,6 +1082,35 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 8D9A38432D194A3D009A4186 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D9A38552D194A41009A4186 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D9A385F2D194A41009A4186 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D9A38742D196510009A4186 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D9A38852D19D427009A4186 /* ArgumentParser in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8DD98C3E2CC592540062D678 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1040,6 +1158,9 @@ 8DD98C542CC5942F0062D678 /* Radio */, 8DD98C502CC5935B0062D678 /* LA */, 8DD98C432CC592540062D678 /* PrySDR */, + 8D9A38472D194A3D009A4186 /* WaterfallFile_UI */, + 8D9A385B2D194A41009A4186 /* WaterfallFile_UITests */, + 8D9A38652D194A41009A4186 /* WaterfallFile_UIUITests */, 8DD98C422CC592540062D678 /* Products */, 8DD98C642CC599CA0062D678 /* Series */, 8DD98C7F2CC7852E0062D678 /* Images */, @@ -1065,6 +1186,10 @@ 8D4068362CFDA69C0064C96D /* airspyhf_iq */, 8D4068452CFDA6B80064C96D /* airspy_iq */, 8D9A33442D0B0D8D009A4186 /* bladerf_iq */, + 8D9A38462D194A3D009A4186 /* WaterfallFile_UI.app */, + 8D9A38582D194A41009A4186 /* WaterfallFile_UITests.xctest */, + 8D9A38622D194A41009A4186 /* WaterfallFile_UIUITests.xctest */, + 8D9A38772D196510009A4186 /* waterfall_file */, ); name = Products; sourceTree = "<group>"; @@ -1384,6 +1509,94 @@ productReference = 8D9A33442D0B0D8D009A4186 /* bladerf_iq */; productType = "com.apple.product-type.tool"; }; + 8D9A38452D194A3D009A4186 /* WaterfallFile_UI */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D9A386A2D194A41009A4186 /* Build configuration list for PBXNativeTarget "WaterfallFile_UI" */; + buildPhases = ( + 8D9A38422D194A3D009A4186 /* Sources */, + 8D9A38432D194A3D009A4186 /* Frameworks */, + 8D9A38442D194A3D009A4186 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 8D9A38472D194A3D009A4186 /* WaterfallFile_UI */, + ); + name = WaterfallFile_UI; + packageProductDependencies = ( + ); + productName = WaterfallFile_UI; + productReference = 8D9A38462D194A3D009A4186 /* WaterfallFile_UI.app */; + productType = "com.apple.product-type.application"; + }; + 8D9A38572D194A41009A4186 /* WaterfallFile_UITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D9A386D2D194A41009A4186 /* Build configuration list for PBXNativeTarget "WaterfallFile_UITests" */; + buildPhases = ( + 8D9A38542D194A41009A4186 /* Sources */, + 8D9A38552D194A41009A4186 /* Frameworks */, + 8D9A38562D194A41009A4186 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8D9A385A2D194A41009A4186 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 8D9A385B2D194A41009A4186 /* WaterfallFile_UITests */, + ); + name = WaterfallFile_UITests; + packageProductDependencies = ( + ); + productName = WaterfallFile_UITests; + productReference = 8D9A38582D194A41009A4186 /* WaterfallFile_UITests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 8D9A38612D194A41009A4186 /* WaterfallFile_UIUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D9A38702D194A41009A4186 /* Build configuration list for PBXNativeTarget "WaterfallFile_UIUITests" */; + buildPhases = ( + 8D9A385E2D194A41009A4186 /* Sources */, + 8D9A385F2D194A41009A4186 /* Frameworks */, + 8D9A38602D194A41009A4186 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8D9A38642D194A41009A4186 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 8D9A38652D194A41009A4186 /* WaterfallFile_UIUITests */, + ); + name = WaterfallFile_UIUITests; + packageProductDependencies = ( + ); + productName = WaterfallFile_UIUITests; + productReference = 8D9A38622D194A41009A4186 /* WaterfallFile_UIUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 8D9A38762D196510009A4186 /* WaterfallFile */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D9A387B2D196510009A4186 /* Build configuration list for PBXNativeTarget "WaterfallFile" */; + buildPhases = ( + 8D9A38732D196510009A4186 /* Sources */, + 8D9A38742D196510009A4186 /* Frameworks */, + 8D9A38752D196510009A4186 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WaterfallFile; + packageProductDependencies = ( + 8D9A38842D19D427009A4186 /* ArgumentParser */, + ); + productName = WaterfallFile; + productReference = 8D9A38772D196510009A4186 /* waterfall_file */; + productType = "com.apple.product-type.tool"; + }; 8DD98C402CC592540062D678 /* PrySDR */ = { isa = PBXNativeTarget; buildConfigurationList = 8DD98C482CC592540062D678 /* Build configuration list for PBXNativeTarget "PrySDR" */; @@ -1444,7 +1657,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1610; + LastUpgradeCheck = 1620; TargetAttributes = { 8D0349172CF70DCD0026DA77 = { CreatedOnToolsVersion = 16.1; @@ -1491,6 +1704,20 @@ 8D9A33432D0B0D8D009A4186 = { CreatedOnToolsVersion = 16.2; }; + 8D9A38452D194A3D009A4186 = { + CreatedOnToolsVersion = 16.2; + }; + 8D9A38572D194A41009A4186 = { + CreatedOnToolsVersion = 16.2; + TestTargetID = 8D9A38452D194A3D009A4186; + }; + 8D9A38612D194A41009A4186 = { + CreatedOnToolsVersion = 16.2; + TestTargetID = 8D9A38452D194A3D009A4186; + }; + 8D9A38762D196510009A4186 = { + CreatedOnToolsVersion = 16.2; + }; 8DD98C402CC592540062D678 = { CreatedOnToolsVersion = 16.0; }; @@ -1532,11 +1759,36 @@ 8D4068352CFDA69C0064C96D /* AirSpyHFIQ */, 8D4068442CFDA6B80064C96D /* AirSpyIQ */, 8D9A33432D0B0D8D009A4186 /* BladeRFIQ */, + 8D9A38452D194A3D009A4186 /* WaterfallFile_UI */, + 8D9A38572D194A41009A4186 /* WaterfallFile_UITests */, + 8D9A38612D194A41009A4186 /* WaterfallFile_UIUITests */, + 8D9A38762D196510009A4186 /* WaterfallFile */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 8D9A38442D194A3D009A4186 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D9A38562D194A41009A4186 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D9A38602D194A41009A4186 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8DD98C702CC632040062D678 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1638,6 +1890,34 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 8D9A38422D194A3D009A4186 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D9A38542D194A41009A4186 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D9A385E2D194A41009A4186 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D9A38732D196510009A4186 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8DD98C3D2CC592540062D678 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1755,6 +2035,26 @@ target = 8D87709C2CD6B4BB0082EC54 /* libbladerf */; targetProxy = 8D87711B2CD79EAD0082EC54 /* PBXContainerItemProxy */; }; + 8D9A385A2D194A41009A4186 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D9A38452D194A3D009A4186 /* WaterfallFile_UI */; + targetProxy = 8D9A38592D194A41009A4186 /* PBXContainerItemProxy */; + }; + 8D9A38642D194A41009A4186 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D9A38452D194A3D009A4186 /* WaterfallFile_UI */; + targetProxy = 8D9A38632D194A41009A4186 /* PBXContainerItemProxy */; + }; + 8D9A38872D19D4DC009A4186 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D9A33432D0B0D8D009A4186 /* BladeRFIQ */; + targetProxy = 8D9A38862D19D4DC009A4186 /* PBXContainerItemProxy */; + }; + 8D9A38892D19D4E1009A4186 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D9A38762D196510009A4186 /* WaterfallFile */; + targetProxy = 8D9A38882D19D4E1009A4186 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -1762,6 +2062,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; MACOSX_DEPLOYMENT_TARGET = 15.1; @@ -1774,6 +2075,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; MACOSX_DEPLOYMENT_TARGET = 15.1; @@ -1786,6 +2088,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; MACOSX_DEPLOYMENT_TARGET = 15.1; @@ -1798,6 +2101,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; MACOSX_DEPLOYMENT_TARGET = 15.1; @@ -1810,6 +2114,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; MACOSX_DEPLOYMENT_TARGET = 15.1; @@ -1822,6 +2127,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; MACOSX_DEPLOYMENT_TARGET = 15.1; @@ -1835,6 +2141,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1850,6 +2157,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1864,6 +2172,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1879,6 +2188,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1893,6 +2203,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1908,6 +2219,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1921,6 +2233,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1932,6 +2245,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1943,6 +2257,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = test_rtlsdr; @@ -1954,6 +2269,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = test_rtlsdr; @@ -1965,6 +2281,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = test_airspy; @@ -1976,6 +2293,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = test_airspy; @@ -1987,6 +2305,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = test_airspyhf; @@ -1998,6 +2317,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = test_airspyhf; @@ -2009,6 +2329,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -2018,6 +2339,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -2027,6 +2349,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = test_bladerf; @@ -2039,6 +2362,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = test_bladerf; @@ -2050,6 +2374,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; "HEADER_SEARCH_PATHS[arch=*]" = "$(PROJECT_DIR)/Radio/HW/BladeRF/src"; @@ -2065,6 +2390,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; EXECUTABLE_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -2077,6 +2403,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; MACOSX_DEPLOYMENT_TARGET = 15.1; @@ -2089,6 +2416,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; MACOSX_DEPLOYMENT_TARGET = 15.1; @@ -2097,6 +2425,156 @@ }; name = Release; }; + 8D9A386B2D194A41009A4186 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = WaterfallFile_UI/WaterfallFile_UI.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"WaterfallFile_UI/Preview Content\""; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 15.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "RadioTeam.WaterfallFile-UI"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 8D9A386C2D194A41009A4186 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = WaterfallFile_UI/WaterfallFile_UI.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"WaterfallFile_UI/Preview Content\""; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 15.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "RadioTeam.WaterfallFile-UI"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 8D9A386E2D194A41009A4186 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "RadioTeam.WaterfallFile-UITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WaterfallFile_UI.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/WaterfallFile_UI"; + }; + name = Debug; + }; + 8D9A386F2D194A41009A4186 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "RadioTeam.WaterfallFile-UITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WaterfallFile_UI.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/WaterfallFile_UI"; + }; + name = Release; + }; + 8D9A38712D194A41009A4186 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "RadioTeam.WaterfallFile-UIUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = WaterfallFile_UI; + }; + name = Debug; + }; + 8D9A38722D194A41009A4186 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "RadioTeam.WaterfallFile-UIUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = WaterfallFile_UI; + }; + name = Release; + }; + 8D9A387C2D196510009A4186 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + ENABLE_HARDENED_RUNTIME = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + PRODUCT_NAME = waterfall_file; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 8D9A387D2D196510009A4186 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 53B26AJZ4Z; + ENABLE_HARDENED_RUNTIME = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + PRODUCT_NAME = waterfall_file; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 8DD98C462CC592540062D678 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2131,6 +2609,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -2194,6 +2673,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -2219,6 +2699,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = prysdr; @@ -2230,6 +2711,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; ENABLE_HARDENED_RUNTIME = YES; PRODUCT_NAME = prysdr; @@ -2242,6 +2724,7 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; @@ -2257,6 +2740,7 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 53B26AJZ4Z; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; @@ -2396,6 +2880,42 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 8D9A386A2D194A41009A4186 /* Build configuration list for PBXNativeTarget "WaterfallFile_UI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D9A386B2D194A41009A4186 /* Debug */, + 8D9A386C2D194A41009A4186 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D9A386D2D194A41009A4186 /* Build configuration list for PBXNativeTarget "WaterfallFile_UITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D9A386E2D194A41009A4186 /* Debug */, + 8D9A386F2D194A41009A4186 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D9A38702D194A41009A4186 /* Build configuration list for PBXNativeTarget "WaterfallFile_UIUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D9A38712D194A41009A4186 /* Debug */, + 8D9A38722D194A41009A4186 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D9A387B2D196510009A4186 /* Build configuration list for PBXNativeTarget "WaterfallFile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D9A387C2D196510009A4186 /* Debug */, + 8D9A387D2D196510009A4186 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 8DD98C3C2CC592540062D678 /* Build configuration list for PBXProject "PrySDR" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -2462,6 +2982,11 @@ package = 8D0349232CF70E180026DA77 /* XCRemoteSwiftPackageReference "swift-argument-parser" */; productName = ArgumentParser; }; + 8D9A38842D19D427009A4186 /* ArgumentParser */ = { + isa = XCSwiftPackageProductDependency; + package = 8D0349232CF70E180026DA77 /* XCRemoteSwiftPackageReference "swift-argument-parser" */; + productName = ArgumentParser; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 8DD98C392CC592540062D678 /* Project object */; diff --git a/Radio/Utils/WaterfallFile/NaiveFFT.swift b/Radio/Utils/WaterfallFile/NaiveFFT.swift new file mode 100644 index 0000000..79393b7 --- /dev/null +++ b/Radio/Utils/WaterfallFile/NaiveFFT.swift @@ -0,0 +1,7 @@ +// +// NaiveFFT.swift +// PrySDR +// +// Created by Jacky Jack on 05/01/2025. +// + diff --git a/Radio/Utils/WaterfallFile/SimpleImage.swift b/Radio/Utils/WaterfallFile/SimpleImage.swift new file mode 100644 index 0000000..c05709f --- /dev/null +++ b/Radio/Utils/WaterfallFile/SimpleImage.swift @@ -0,0 +1,116 @@ +// +// Image.swift +// PrySDR +// +// Created by Jacky Jack on 27/12/2024. +// + +import Foundation +import AppKit + +// https://stackoverflow.com/questions/30958427/pixel-array-to-uiimage-in-swift +public struct PixelData { + var a: UInt8 + var r: UInt8 + var g: UInt8 + var b: UInt8 +} + +public class SimpleImage { + + private var pixels: [PixelData] = [] + private var width:Int = 0 + private var height:Int = 0 + + init(width: Int, height: Int) { + self.width = width + self.height = height + pixels = .init(repeating: .init(a: 255, r: 0, g: 0, b: 0), count: width*height) + } + + + func drawPalletLine(line: Int, pixelLine:[Float]) { + for i in 0..<self.width { + var pixel = pixelLine[i] + if pixel>256.0 { + //print("values to high") + pixel = 255.0 + } + if pixel < 0.0 { + pixel = 0.0 + } + self.pixels[line*self.width + i].a = 255 + self.pixels[line*self.width + i].r = UInt8(pixel) + self.pixels[line*self.width + i].g = UInt8(pixel) + self.pixels[line*self.width + i].b = UInt8(pixel) + } + } + + func drawPixel(_ w: Int, _ h: Int, _ pixel: PixelData) { + self.pixels[h*self.height+w] = pixel + } + + func toCGImage() -> CGImage? { + guard self.width > 0 && self.height > 0 else { return nil } + guard self.pixels.count == self.width * self.height else { return nil } + + let rgbColorSpace = CGColorSpaceCreateDeviceRGB() + let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue) + let bitsPerComponent = 8 + let bitsPerPixel = 32 + + var data = self.pixels // Copy to mutable [] + guard let providerRef = CGDataProvider(data: NSData(bytes: &data, + length: data.count * MemoryLayout<PixelData>.size) + ) + else { return nil } + + guard let cgim = CGImage( + width: self.width, + height: self.height, + bitsPerComponent: bitsPerComponent, + bitsPerPixel: bitsPerPixel, + bytesPerRow: self.width * MemoryLayout<PixelData>.size, + space: rgbColorSpace, + bitmapInfo: bitmapInfo, + provider: providerRef, + decode: nil, + shouldInterpolate: true, + intent: .defaultIntent + ) + else { return nil } + + return cgim + } + + func toNSImage() -> NSImage? { + guard let cgim = toCGImage() else { return nil } + return NSImage(cgImage: cgim, size:.zero) + } + + func saveAsJPEG(_ image: NSImage, _ name: String) { + if let imageData = image.tiffRepresentation { + do { + let bundlePath = Bundle.main.resourcePath! + let imageUrl = URL(fileURLWithPath:bundlePath+"/"+name) + try imageData.write(to: imageUrl) + + } catch { + print("Cant save image to disk") + } + + } else { + print("cant get tiff representation") + } + } + + func saveAsJPEG(_ name: String) { + if let img = self.toNSImage() { + saveAsJPEG(img, name) + } + } +} + + + + diff --git a/Radio/Utils/WaterfallFile/main.swift b/Radio/Utils/WaterfallFile/main.swift new file mode 100644 index 0000000..cf72d56 --- /dev/null +++ b/Radio/Utils/WaterfallFile/main.swift @@ -0,0 +1,113 @@ +// +// main.swift +// WaterfallFile +// +// Created by Jacky Jack on 23/12/2024. +// + +import Foundation +import Accelerate + +print("Read binary file") + +let input_filename="rtlsdr_100M_m.cu8" + + +//get data from u8 file +let dir = getCurrentExecutableDir() +let filemgr = FileManager.default + +if !filemgr.fileExists(atPath: input_filename) { + print("Cant find file \(input_filename)") + exit(0) +} + +let fileHandle: FileHandle? = FileHandle(forReadingAtPath: dir+"/"+input_filename) +var i8_arr:[Int8]? = nil +if let file = fileHandle { + file.seek(toFileOffset: 0) + do { + if let databuf = try file.readToEnd() { + + print("read \(databuf.count) \(databuf) bytes") + //let temp_arr = [Int8](databuf) + i8_arr = [Int8](repeating: 0, count: databuf.count) + + for i in 0..<i8_arr!.count { + print(String(format:"%02X", databuf[i]),terminator: "") + //convert from 0..255 to -127..128 + let val = databuf[i] + if val <= 127 { + i8_arr![i] = Int8(val)-127 + } else { + i8_arr![i] = Int8(val-128) + } + //print(String(format:"%02X", i8_arr![i]),terminator: "") + } + } + } catch { + print(error) + exit(0) + } + +} +fileHandle?.closeFile() + +//all data in buffer lets process data +//convert all u8 data to float's + +//is there other ways to do that? +/*for i in 0..<dataFloat.count { + dataFloat[i] = Float(u8_arr![i]) +}*/ +//will generate 512x512 image +let sampleCount = 512 +let img = SimpleImage(width: sampleCount, height: sampleCount) +let number_of_lines = (i8_arr!.count/sampleCount) +let forwardDCT = vDSP.DCT(count: sampleCount, transformType: .II)! +var frequencyDomain:[Float] = [] +var transform_result:[Float] = .init(repeating: 0.0, count: sampleCount) +var dataFloat = [Float](repeating: 0.0, count: sampleCount) +for i in 0..<sampleCount { + let sample_idx = i*sampleCount + if i < number_of_lines { + let processingSlice = i8_arr![sample_idx...sample_idx+512-1] + let processingArray = Array(processingSlice) + vDSP.convertElements(of: processingArray, to: &dataFloat) + //dataFloat = vDSP.add(127.0, dataFloat) + //print(dataFloat) + + //move from -127.0 to 128.0 range -1.0...1.0 + //var adjusted = vDSP.divide(dataFloat, Float(sampleCount)) + var adjusted = dataFloat + //print(adjusted) + + transform_result = forwardDCT.transform(adjusted) + transform_result = vDSP.absolute(transform_result) + let max = vDSP.maximum(transform_result) + //print("max=\(max)") + //frequencyDomain.append(contentsOf: transform_result) + img.drawPalletLine(line: i, pixelLine: transform_result) + //print(transform_result) + } else { + img.drawPalletLine(line: i, pixelLine: transform_result) + } + +} + +img.drawPixel(0, 0, PixelData(a: 255, r: 255, g: 0, b: 0)) +img.drawPixel(sampleCount-1, 0, PixelData(a: 255, r: 0, g: 255, b: 0)) +img.drawPixel(0, sampleCount-1, PixelData(a: 255, r: 0, g: 255, b: 0)) +img.drawPixel(sampleCount-1, sampleCount-1, PixelData(a: 255, r: 0, g: 0, b: 255)) + + +//print(dataFloat) + + + +//output data to image file +img.saveAsJPEG("fft_100m_512.jpeg") + + + +print("All computations are done") diff --git a/WaterfallFile_UI/Assets.xcassets/AccentColor.colorset/Contents.json b/WaterfallFile_UI/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/WaterfallFile_UI/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallFile_UI/Assets.xcassets/AppIcon.appiconset/Contents.json b/WaterfallFile_UI/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/WaterfallFile_UI/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallFile_UI/Assets.xcassets/Contents.json b/WaterfallFile_UI/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/WaterfallFile_UI/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallFile_UI/ContentView.swift b/WaterfallFile_UI/ContentView.swift new file mode 100644 index 0000000..92445d8 --- /dev/null +++ b/WaterfallFile_UI/ContentView.swift @@ -0,0 +1,94 @@ +// +// ContentView.swift +// WaterfallFile_UI +// +// Created by Jacky Jack on 23/12/2024. +// + +import SwiftUI +import SwiftData +import AppKit + + +struct ContentView: View { + @Environment(\.modelContext) private var modelContext + + @EnvironmentObject var fileSpectrum: FileSpectrum + + @State private var image: Image? + + @Query private var items: [Item] + + var body: some View { + /* + NavigationSplitView { + List { + ForEach(items) { item in + NavigationLink { + Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") + } label: { + Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) + } + } + .onDelete(perform: deleteItems) + } + .navigationSplitViewColumnWidth(min: 180, ideal: 200) + .toolbar { + ToolbarItem { + Button(action: addItem) { + Label("Add Item", systemImage: "plus") + } + } + } + } detail: { + Text("Select an item") + } + */ + //Image(decorative: fileSpectrum.makeSpectrogramImage(), scale: 1.0, orientation: .left) + VStack { + //Image(nsImage:fileSpectrum.outputImage) + Image(decorative: fileSpectrum.outputImage, + scale: 1, + orientation: .left) + //.resizable() + + + //Image(fileSpectrum.makeSpectrogramImage(), scale: 1, orientation:.left, label: "Image") + //Image(decorative: fileSpectrum.makeSpectrogramImage(), scale: 1.0, orientation: .left) + //Image(nsImage: fileSpectrum.makeSpectrogramImage()) + HStack { + Text("Hello world") + .padding() + Button("Start") { + print("Start") + } + Button("Stop") { + print("Stop") + } + Button("Load") { + print("Load") + } + } + } + } + + private func addItem() { + withAnimation { + let newItem = Item(timestamp: Date()) + modelContext.insert(newItem) + } + } + + private func deleteItems(offsets: IndexSet) { + withAnimation { + for index in offsets { + modelContext.delete(items[index]) + } + } + } +} + +#Preview { + ContentView() + .modelContainer(for: Item.self, inMemory: true) +} diff --git a/WaterfallFile_UI/FileSpectrum.swift b/WaterfallFile_UI/FileSpectrum.swift new file mode 100644 index 0000000..6415dfa --- /dev/null +++ b/WaterfallFile_UI/FileSpectrum.swift @@ -0,0 +1,64 @@ +// +// FileSpectrum.swift +// PrySDR +// +// Created by Jacky Jack on 01/01/2025. +// + +import Accelerate +import AppKit +import Combine +import CoreImage + + +class FileSpectrum: NSObject, ObservableObject { + + let default_width = 512 + let default_height = 512 + + let sessionQueue = DispatchQueue(label: "sessionQueue", + attributes: [], + autoreleaseFrequency: .workItem) + + @Published var outputImage = emptyCGImage + + let simpleImage = SimpleImage(width: 256, height: 256) + + /// A 1x1 Core Graphics image. + static var emptyCGImage: CGImage = { + let buffer = vImage.PixelBuffer( + pixelValues: [0], + size: .init(width: 1, height: 1), + pixelFormat: vImage.Planar8.self) + + let fmt = vImage_CGImageFormat( + bitsPerComponent: 8, + bitsPerPixel: 8 , + colorSpace: CGColorSpaceCreateDeviceGray(), + bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue), + renderingIntent: .defaultIntent) + + return buffer.makeCGImage(cgImageFormat: fmt!)! + }() + + func makeSpectrogramImage() -> NSImage { + + return NSImage(cgImage: FileSpectrum.emptyCGImage, size: .zero) + } + + + + func startRunning() { + sessionQueue.async { + print("lets start the task for spectrum analysis") + for i in 0..<100 { + print("Process the task \(i)") + self.simpleImage.drawPixel(i, i, PixelData(a: 255, r: 255, g: 0, b: 0)) + DispatchQueue.main.async { + self.outputImage = self.simpleImage.toCGImage()! + } + sleep(1) + } + } + } +} diff --git a/WaterfallFile_UI/Item.swift b/WaterfallFile_UI/Item.swift new file mode 100644 index 0000000..f20bc8d --- /dev/null +++ b/WaterfallFile_UI/Item.swift @@ -0,0 +1,18 @@ +// +// Item.swift +// WaterfallFile_UI +// +// Created by Jacky Jack on 23/12/2024. +// + +import Foundation +import SwiftData + +@Model +final class Item { + var timestamp: Date + + init(timestamp: Date) { + self.timestamp = timestamp + } +} diff --git a/WaterfallFile_UI/Preview Content/Preview Assets.xcassets/Contents.json b/WaterfallFile_UI/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/WaterfallFile_UI/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WaterfallFile_UI/WaterfallFile_UI.entitlements b/WaterfallFile_UI/WaterfallFile_UI.entitlements new file mode 100644 index 0000000..18aff0c --- /dev/null +++ b/WaterfallFile_UI/WaterfallFile_UI.entitlements @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.security.app-sandbox</key> + <true/> + <key>com.apple.security.files.user-selected.read-only</key> + <true/> +</dict> +</plist> diff --git a/WaterfallFile_UI/WaterfallFile_UIApp.swift b/WaterfallFile_UI/WaterfallFile_UIApp.swift new file mode 100644 index 0000000..959fe55 --- /dev/null +++ b/WaterfallFile_UI/WaterfallFile_UIApp.swift @@ -0,0 +1,45 @@ +// +// WaterfallFile_UIApp.swift +// WaterfallFile_UI +// +// Created by Jacky Jack on 23/12/2024. +// + +import SwiftUI +import SwiftData + +@main +struct WaterfallFile_UIApp: App { + + let fileSpectrum = FileSpectrum() + + @Environment(\.scenePhase) private var scenePhase + + var sharedModelContainer: ModelContainer = { + let schema = Schema([ + Item.self, + ]) + let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) + + do { + return try ModelContainer(for: schema, configurations: [modelConfiguration]) + } catch { + fatalError("Could not create ModelContainer: \(error)") + } + }() + + var body: some Scene { + WindowGroup { + ContentView() + .environmentObject(fileSpectrum) + .onChange(of: scenePhase) { phase in + if phase == .active { + Task(priority: .userInitiated) { + fileSpectrum.startRunning() + } + } + } + } + .modelContainer(sharedModelContainer) + } +} diff --git a/WaterfallFile_UITests/WaterfallFile_UITests.swift b/WaterfallFile_UITests/WaterfallFile_UITests.swift new file mode 100644 index 0000000..1338c5e --- /dev/null +++ b/WaterfallFile_UITests/WaterfallFile_UITests.swift @@ -0,0 +1,17 @@ +// +// WaterfallFile_UITests.swift +// WaterfallFile_UITests +// +// Created by Jacky Jack on 23/12/2024. +// + +import Testing +@testable import WaterfallFile_UI + +struct WaterfallFile_UITests { + + @Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + } + +} diff --git a/WaterfallFile_UIUITests/WaterfallFile_UIUITests.swift b/WaterfallFile_UIUITests/WaterfallFile_UIUITests.swift new file mode 100644 index 0000000..c3348da --- /dev/null +++ b/WaterfallFile_UIUITests/WaterfallFile_UIUITests.swift @@ -0,0 +1,43 @@ +// +// WaterfallFile_UIUITests.swift +// WaterfallFile_UIUITests +// +// Created by Jacky Jack on 23/12/2024. +// + +import XCTest + +final class WaterfallFile_UIUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + @MainActor + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + @MainActor + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/WaterfallFile_UIUITests/WaterfallFile_UIUITestsLaunchTests.swift b/WaterfallFile_UIUITests/WaterfallFile_UIUITestsLaunchTests.swift new file mode 100644 index 0000000..99aaf0c --- /dev/null +++ b/WaterfallFile_UIUITests/WaterfallFile_UIUITestsLaunchTests.swift @@ -0,0 +1,33 @@ +// +// WaterfallFile_UIUITestsLaunchTests.swift +// WaterfallFile_UIUITests +// +// Created by Jacky Jack on 23/12/2024. +// + +import XCTest + +final class WaterfallFile_UIUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + @MainActor + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} |