diff --git a/.gitignore b/.gitignore index cd937fe..b3eeb66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .DS_Store /.build -/Packages xcuserdata/ DerivedData/ .swiftpm/configuration/registries.json diff --git a/Package.swift b/Package.swift index a24ecaa..030afe0 100644 --- a/Package.swift +++ b/Package.swift @@ -9,35 +9,22 @@ let package = Package( // Products define the executables and libraries a package produces, making them visible to other packages. .library( name: "EcgSynKit", - targets: ["EcgSynKit"]), + targets: ["EcgSynKit"] + ), ], dependencies: [ .package(url: "https://github.com/apple/swift-algorithms", from: "1.2.0"), - // .package(url: "https://github.com/AudioKit/KissFFT.git", from: "1.0.0") - .package(path: "./Packages/KissFFT/") + .package(path: "./Packages/KissFFT/"), + .package(path: "./Packages/PFFFT/"), ], targets: [ - // Targets are the basic building blocks of a package, defining a module or a test suite. - // Targets can depend on other targets in this package and products from dependencies. .target( - name: "PFFFT", - publicHeadersPath: "include", - cSettings: [ - .define("PFFFT_SCALVEC_ENABLED", to: "1"), - .define("_USE_MATH_DEFINES"), - .define("NDEBUG"), - .unsafeFlags(["-O3", "-std=c99"]), - .unsafeFlags(["-mavx"], .when(platforms: [.linux, .macOS])), + name: "EcgSynKit", + dependencies: [ + .product(name: "PFFFT", package: "PFFFT"), + .product(name: "Algorithms", package: "swift-algorithms"), ] ), - .target( - name: "EcgSynKit", - dependencies: [ - .target(name: "PFFFT"), - .product(name: "Algorithms", package: "swift-algorithms"), - .product(name: "KissFFT", package: "kissfft") - ] - ), .testTarget( name: "EcgSynKitTests", dependencies: ["EcgSynKit"] diff --git a/Packages/PFFFT/.gitignore b/Packages/PFFFT/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/Packages/PFFFT/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Packages/PFFFT/Package.swift b/Packages/PFFFT/Package.swift new file mode 100644 index 0000000..6b05beb --- /dev/null +++ b/Packages/PFFFT/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "PFFFT", + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "PFFFT", + targets: ["PFFFT", "PFFFTLib"] + ), + ], + targets: [ + .target( + name: "PFFFTLib", + publicHeadersPath: "include", + cSettings: [ + .define("PFFFT_SCALVEC_ENABLED", to: "1"), + .define("_USE_MATH_DEFINES"), + .define("NDEBUG"), + .unsafeFlags(["-O3", "-std=c99"]), + ] + ), + .target( + name: "PFFFT", + dependencies: ["PFFFTLib"] + ), + .testTarget( + name: "PFFFTTests", + dependencies: ["PFFFTLib"] + ), + ] +) diff --git a/Packages/PFFFT/Sources/PFFFT/Buffer.swift b/Packages/PFFFT/Sources/PFFFT/Buffer.swift new file mode 100644 index 0000000..d602ba2 --- /dev/null +++ b/Packages/PFFFT/Sources/PFFFT/Buffer.swift @@ -0,0 +1,29 @@ +let bufferAlignment = 32 + +@frozen +public struct Buffer: ~Copyable { + let buffer: UnsafeMutableBufferPointer + + init(unsafeUninitializedCapacity: Int) { + buffer = UnsafeMutableRawBufferPointer.allocate( + byteCount: MemoryLayout.stride * unsafeUninitializedCapacity, + alignment: bufferAlignment + ).bindMemory(to: Element.self) + } + + deinit { + buffer.deallocate() + } + + func withUnsafeMutableBufferPointer(_ body: (UnsafeMutableBufferPointer) throws -> R) rethrows -> R { + try body(buffer) + } + + func withUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R { + try body(UnsafeBufferPointer(buffer)) + } + + func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R { + try body(UnsafeMutableRawBufferPointer(buffer)) + } +} diff --git a/Packages/PFFFT/Sources/PFFFT/FFTDoubleImpl.swift b/Packages/PFFFT/Sources/PFFFT/FFTDoubleImpl.swift new file mode 100644 index 0000000..172aefb --- /dev/null +++ b/Packages/PFFFT/Sources/PFFFT/FFTDoubleImpl.swift @@ -0,0 +1,57 @@ +internal import PFFFTLib + +struct FFTDoubleImpl: FFTImplProtocol { + let ptr: OpaquePointer + + init?(n: Int, type: FFTType) { + guard let ptr = pffftd_new_setup(Int32(n), pffft_transform_t(type)) else { + return nil + } + self.ptr = ptr + } + + func fft(input: borrowing Buffer, output: borrowing Buffer, work: borrowing Buffer?, sign: FFTSign) { + let work: UnsafeMutablePointer! = switch work { + case let .some(b): b.buffer.baseAddress + case .none: nil + } + + pffftd_transform_ordered(ptr, input.buffer.baseAddress, output.buffer.baseAddress, work, pffft_direction_t(sign)) + } + + func fftUnordered(input: borrowing Buffer, output: borrowing Buffer, work: borrowing Buffer?, sign: FFTSign) { + let work: UnsafeMutablePointer! = switch work { + case let .some(b): b.buffer.baseAddress + case .none: nil + } + pffftd_transform(ptr, input.buffer.baseAddress, output.buffer.baseAddress, work, pffft_direction_t(sign)) + } + + func zReorder(input: borrowing Buffer, output: borrowing Buffer, sign: FFTSign) { + pffftd_zreorder(ptr, input.buffer.baseAddress, output.buffer.baseAddress, pffft_direction_t(sign)) + } + + func zConvolveAccumulate(dftA: borrowing Buffer, dftB: borrowing Buffer, dftAB: borrowing Buffer, scaling: Double) { + pffftd_zconvolve_accumulate(ptr, dftA.buffer.baseAddress, dftB.buffer.baseAddress, dftAB.buffer.baseAddress, scaling) + } + + func zConvolve(dftA: borrowing Buffer, dftB: borrowing Buffer, dftAB: borrowing Buffer, scaling: Double) { + pffftd_zconvolve_no_accu(ptr, dftA.buffer.baseAddress, dftB.buffer.baseAddress, dftAB.buffer.baseAddress, scaling) + } + + func minFftSize(for type: FFTType) -> Int { + Int(pffftd_min_fft_size(pffft_transform_t(type))) + } + + static func isValidSize(_ n: Int, for type: FFTType) -> Bool { + pffftd_is_valid_size(Int32(n), pffft_transform_t(type)) != 0 + } + + static func nearestValidSize(_ n: Int, for type: FFTType, higher: Bool) -> Int { + Int(pffftd_nearest_transform_size(Int32(n), pffft_transform_t(type), higher ? 1 : 0)) + } + + static func simdArch() -> String { + String(cString: pffftd_simd_arch()) + } +} diff --git a/Packages/PFFFT/Sources/PFFFT/FFTFloatImpl.swift b/Packages/PFFFT/Sources/PFFFT/FFTFloatImpl.swift new file mode 100644 index 0000000..ef6d897 --- /dev/null +++ b/Packages/PFFFT/Sources/PFFFT/FFTFloatImpl.swift @@ -0,0 +1,57 @@ +internal import PFFFTLib + +struct FFTFloatImpl: FFTImplProtocol { + let ptr: OpaquePointer + + init?(n: Int, type: FFTType) { + guard let ptr = pffft_new_setup(Int32(n), pffft_transform_t(type)) else { + return nil + } + self.ptr = ptr + } + + func fft(input: borrowing Buffer, output: borrowing Buffer, work: borrowing Buffer?, sign: FFTSign) { + let work: UnsafeMutablePointer! = switch work { + case let .some(b): b.buffer.baseAddress + case .none: nil + } + + pffft_transform_ordered(ptr, input.buffer.baseAddress, output.buffer.baseAddress, work, pffft_direction_t(sign)) + } + + func fftUnordered(input: borrowing Buffer, output: borrowing Buffer, work: borrowing Buffer?, sign: FFTSign) { + let work: UnsafeMutablePointer! = switch work { + case let .some(b): b.buffer.baseAddress + case .none: nil + } + pffft_transform(ptr, input.buffer.baseAddress, output.buffer.baseAddress, work, pffft_direction_t(sign)) + } + + func zReorder(input: borrowing Buffer, output: borrowing Buffer, sign: FFTSign) { + pffft_zreorder(ptr, input.buffer.baseAddress, output.buffer.baseAddress, pffft_direction_t(sign)) + } + + func zConvolveAccumulate(dftA: borrowing Buffer, dftB: borrowing Buffer, dftAB: borrowing Buffer, scaling: Float) { + pffft_zconvolve_accumulate(ptr, dftA.buffer.baseAddress, dftB.buffer.baseAddress, dftAB.buffer.baseAddress, scaling) + } + + func zConvolve(dftA: borrowing Buffer, dftB: borrowing Buffer, dftAB: borrowing Buffer, scaling: Float) { + pffft_zconvolve_no_accu(ptr, dftA.buffer.baseAddress, dftB.buffer.baseAddress, dftAB.buffer.baseAddress, scaling) + } + + func minFftSize(for type: FFTType) -> Int { + Int(pffft_min_fft_size(pffft_transform_t(type))) + } + + static func isValidSize(_ n: Int, for type: FFTType) -> Bool { + pffft_is_valid_size(Int32(n), pffft_transform_t(type)) != 0 + } + + static func nearestValidSize(_ n: Int, for type: FFTType, higher: Bool) -> Int { + Int(pffft_nearest_transform_size(Int32(n), pffft_transform_t(type), higher ? 1 : 0)) + } + + static func simdArch() -> String { + String(cString: pffft_simd_arch()) + } +} diff --git a/Packages/PFFFT/Sources/PFFFT/PFFFT.swift b/Packages/PFFFT/Sources/PFFFT/PFFFT.swift new file mode 100644 index 0000000..87688ab --- /dev/null +++ b/Packages/PFFFT/Sources/PFFFT/PFFFT.swift @@ -0,0 +1,47 @@ +internal import PFFFTLib + +enum FFTType { + case real + case complex +} + +enum FFTSign: Int { + case forward = -1 + case backward = 1 +} + +protocol FFTImplProtocol { + associatedtype Element + + init?(n: Int, type: FFTType) + + func fft(input: borrowing Buffer, output: borrowing Buffer, work: borrowing Buffer?, sign: FFTSign) + func fftUnordered(input: borrowing Buffer, output: borrowing Buffer, work: borrowing Buffer?, sign: FFTSign) + func zReorder(input: borrowing Buffer, output: borrowing Buffer, sign: FFTSign) + func zConvolveAccumulate(dftA: borrowing Buffer, dftB: borrowing Buffer, dftAB: borrowing Buffer, scaling: Element) + func zConvolve(dftA: borrowing Buffer, dftB: borrowing Buffer, dftAB: borrowing Buffer, scaling: Element) + func minFftSize(for type: FFTType) -> Int + static func isValidSize(_ n: Int, for type: FFTType) -> Bool + static func nearestValidSize(_ n: Int, for type: FFTType, higher: Bool) -> Int + static func simdArch() -> String +} + +extension pffft_transform_t { + @inline(__always) + init(_ type: FFTType) { + switch type { + case .real: self = PFFFT_REAL + case .complex: self = PFFFT_COMPLEX + } + } +} + +extension pffft_direction_t { + @inline(__always) + init(_ sign: FFTSign) { + switch sign { + case .forward: self = PFFFT_FORWARD + case .backward: self = PFFFT_BACKWARD + } + } +} diff --git a/Packages/PFFFT/Sources/PFFFT/SetupCache.swift b/Packages/PFFFT/Sources/PFFFT/SetupCache.swift new file mode 100644 index 0000000..e45afb3 --- /dev/null +++ b/Packages/PFFFT/Sources/PFFFT/SetupCache.swift @@ -0,0 +1,30 @@ +import Foundation + +struct CacheKey : Hashable { + let n: Int + let type: FFTType +} + +class SetupCache : @unchecked Sendable { + private var cache: [CacheKey: Impl?] = [:] + + private let queue = DispatchQueue(label: String(describing: SetupCache.self), attributes: .concurrent) + + func get(for n: Int, type: FFTType) -> Impl? { + var setup: Impl?? + queue.sync { + setup = cache[CacheKey(n: n, type: type)] + } + if setup == nil { + queue.sync(flags: .barrier) { + setup = cache[CacheKey(n: n, type: type)] + if setup == nil { + setup = Impl(n: n, type: type) + cache[CacheKey(n: n, type: type)] = setup + } + } + } + return setup! + } + +} diff --git a/Sources/PFFFT/pffft.h b/Packages/PFFFT/Sources/PFFFTLib/include/pffft.h similarity index 100% rename from Sources/PFFFT/pffft.h rename to Packages/PFFFT/Sources/PFFFTLib/include/pffft.h diff --git a/Sources/PFFFT/include/pffft_double.h b/Packages/PFFFT/Sources/PFFFTLib/include/pffft_double.h similarity index 100% rename from Sources/PFFFT/include/pffft_double.h rename to Packages/PFFFT/Sources/PFFFTLib/include/pffft_double.h diff --git a/Packages/PFFFT/Sources/PFFFTLib/pffft.c b/Packages/PFFFT/Sources/PFFFTLib/pffft.c new file mode 100644 index 0000000..4862a4f --- /dev/null +++ b/Packages/PFFFT/Sources/PFFFTLib/pffft.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) + Copyright (c) 2020 Hayati Ayguen ( h_ayguen@web.de ) + + Based on original fortran 77 code from FFTPACKv4 from NETLIB + (http://www.netlib.org/fftpack), authored by Dr Paul Swarztrauber + of NCAR, in 1985. + + As confirmed by the NCAR fftpack software curators, the following + FFTPACKv5 license applies to FFTPACKv4 sources. My changes are + released under the same terms. + + FFTPACK license: + + http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html + + Copyright (c) 2004 the University Corporation for Atmospheric + Research ("UCAR"). All rights reserved. Developed by NCAR's + Computational and Information Systems Laboratory, UCAR, + www.cisl.ucar.edu. + + Redistribution and use of the Software in source and binary forms, + with or without modification, is permitted provided that the + following conditions are met: + + - Neither the names of NCAR's Computational and Information Systems + Laboratory, the University Corporation for Atmospheric Research, + nor the names of its sponsors or contributors may be used to + endorse or promote products derived from this Software without + specific prior written permission. + + - Redistributions of source code must retain the above copyright + notices, this list of conditions, and the disclaimer below. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer below in the + documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE + SOFTWARE. + + + PFFFT : a Pretty Fast FFT. + + This file is largerly based on the original FFTPACK implementation, modified in + order to take advantage of SIMD instructions of modern CPUs. +*/ + +/* + ChangeLog: + - 2011/10/02, version 1: This is the very first release of this file. +*/ + +#include "pffft.h" + +/* detect compiler flavour */ +#if defined(_MSC_VER) +# define COMPILER_MSVC +#elif defined(__GNUC__) +# define COMPILER_GCC +#endif + +#include +#include +#include +#include +#include + +#if defined(COMPILER_GCC) +# define ALWAYS_INLINE(return_type) inline return_type __attribute__ ((always_inline)) +# define NEVER_INLINE(return_type) return_type __attribute__ ((noinline)) +# define RESTRICT __restrict +# define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ varname__[size__]; +#elif defined(COMPILER_MSVC) +# define ALWAYS_INLINE(return_type) __forceinline return_type +# define NEVER_INLINE(return_type) __declspec(noinline) return_type +# define RESTRICT __restrict +# define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ *varname__ = (type__*)_alloca(size__ * sizeof(type__)) +#endif + + +#ifdef COMPILER_MSVC +#pragma warning( disable : 4244 4305 4204 4456 ) +#endif + +/* + vector support macros: the rest of the code is independant of + SSE/Altivec/NEON -- adding support for other platforms with 4-element + vectors should be limited to these macros +*/ +#include "simd/pf_float.h" + +/* have code comparable with this definition */ +#define SETUP_STRUCT PFFFT_Setup +#define FUNC_NEW_SETUP pffft_new_setup +#define FUNC_DESTROY pffft_destroy_setup +#define FUNC_TRANSFORM_UNORDRD pffft_transform +#define FUNC_TRANSFORM_ORDERED pffft_transform_ordered +#define FUNC_ZREORDER pffft_zreorder +#define FUNC_ZCONVOLVE_ACCUMULATE pffft_zconvolve_accumulate +#define FUNC_ZCONVOLVE_NO_ACCU pffft_zconvolve_no_accu + +#define FUNC_ALIGNED_MALLOC pffft_aligned_malloc +#define FUNC_ALIGNED_FREE pffft_aligned_free +#define FUNC_SIMD_SIZE pffft_simd_size +#define FUNC_MIN_FFT_SIZE pffft_min_fft_size +#define FUNC_IS_VALID_SIZE pffft_is_valid_size +#define FUNC_NEAREST_SIZE pffft_nearest_transform_size +#define FUNC_SIMD_ARCH pffft_simd_arch +#define FUNC_VALIDATE_SIMD_A validate_pffft_simd +#define FUNC_VALIDATE_SIMD_EX validate_pffft_simd_ex + +#define FUNC_CPLX_FINALIZE pffft_cplx_finalize +#define FUNC_CPLX_PREPROCESS pffft_cplx_preprocess +#define FUNC_REAL_PREPROCESS_4X4 pffft_real_preprocess_4x4 +#define FUNC_REAL_PREPROCESS pffft_real_preprocess +#define FUNC_REAL_FINALIZE_4X4 pffft_real_finalize_4x4 +#define FUNC_REAL_FINALIZE pffft_real_finalize +#define FUNC_TRANSFORM_INTERNAL pffft_transform_internal + +#define FUNC_COS cosf +#define FUNC_SIN sinf + + +#include "pffft_priv_impl.h" + + diff --git a/Sources/PFFFT/pffft_common.c b/Packages/PFFFT/Sources/PFFFTLib/pffft_common.c similarity index 100% rename from Sources/PFFFT/pffft_common.c rename to Packages/PFFFT/Sources/PFFFTLib/pffft_common.c diff --git a/Sources/PFFFT/pffft_double.c b/Packages/PFFFT/Sources/PFFFTLib/pffft_double.c similarity index 100% rename from Sources/PFFFT/pffft_double.c rename to Packages/PFFFT/Sources/PFFFTLib/pffft_double.c diff --git a/Sources/PFFFT/pffft_priv_impl.h b/Packages/PFFFT/Sources/PFFFTLib/pffft_priv_impl.h similarity index 100% rename from Sources/PFFFT/pffft_priv_impl.h rename to Packages/PFFFT/Sources/PFFFTLib/pffft_priv_impl.h diff --git a/Sources/PFFFT/simd/pf_altivec_float.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_altivec_float.h similarity index 100% rename from Sources/PFFFT/simd/pf_altivec_float.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_altivec_float.h diff --git a/Sources/PFFFT/simd/pf_avx_double.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_avx_double.h similarity index 100% rename from Sources/PFFFT/simd/pf_avx_double.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_avx_double.h diff --git a/Sources/PFFFT/simd/pf_double.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_double.h similarity index 100% rename from Sources/PFFFT/simd/pf_double.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_double.h diff --git a/Sources/PFFFT/simd/pf_float.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_float.h similarity index 100% rename from Sources/PFFFT/simd/pf_float.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_float.h diff --git a/Sources/PFFFT/simd/pf_neon_double.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_neon_double.h similarity index 100% rename from Sources/PFFFT/simd/pf_neon_double.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_neon_double.h diff --git a/Sources/PFFFT/simd/pf_neon_double_from_avx.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_neon_double_from_avx.h similarity index 100% rename from Sources/PFFFT/simd/pf_neon_double_from_avx.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_neon_double_from_avx.h diff --git a/Sources/PFFFT/simd/pf_neon_float.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_neon_float.h similarity index 100% rename from Sources/PFFFT/simd/pf_neon_float.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_neon_float.h diff --git a/Sources/PFFFT/simd/pf_scalar_double.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_scalar_double.h similarity index 100% rename from Sources/PFFFT/simd/pf_scalar_double.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_scalar_double.h diff --git a/Sources/PFFFT/simd/pf_scalar_float.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_scalar_float.h similarity index 100% rename from Sources/PFFFT/simd/pf_scalar_float.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_scalar_float.h diff --git a/Sources/PFFFT/simd/pf_sse1_float.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_sse1_float.h similarity index 100% rename from Sources/PFFFT/simd/pf_sse1_float.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_sse1_float.h diff --git a/Sources/PFFFT/simd/pf_sse2_double.h b/Packages/PFFFT/Sources/PFFFTLib/simd/pf_sse2_double.h similarity index 100% rename from Sources/PFFFT/simd/pf_sse2_double.h rename to Packages/PFFFT/Sources/PFFFTLib/simd/pf_sse2_double.h diff --git a/Packages/PFFFT/Tests/PFFFTTests/PFFFTTests.swift b/Packages/PFFFT/Tests/PFFFTTests/PFFFTTests.swift new file mode 100644 index 0000000..1ca34d3 --- /dev/null +++ b/Packages/PFFFT/Tests/PFFFTTests/PFFFTTests.swift @@ -0,0 +1,6 @@ +import Testing +@testable import PFFFT + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +} diff --git a/Sources/EcgSynKit/EcgSynKit.swift b/Sources/EcgSynKit/EcgSynKit.swift index d2ab6d1..a3c1b29 100644 --- a/Sources/EcgSynKit/EcgSynKit.swift +++ b/Sources/EcgSynKit/EcgSynKit.swift @@ -1,6 +1,6 @@ import Algorithms import Foundation -import PFFFT +import PFFFTLib struct Parameters { /// The number of beats to simulate. @@ -85,6 +85,10 @@ class PfftSetupCache: @unchecked Sendable { static let shared = PfftSetupCache() } +struct EcgDerive { + let rrpc: [Double] +} + func stdev(_ data: [Double]) -> Double { let n = Double(data.count) let mean = data.reduce(0.0, +) / n diff --git a/Tests/EcgSynKitTests/EcgSynKitTests.swift b/Tests/EcgSynKitTests/EcgSynKitTests.swift index dc2434e..e18059b 100644 --- a/Tests/EcgSynKitTests/EcgSynKitTests.swift +++ b/Tests/EcgSynKitTests/EcgSynKitTests.swift @@ -1,6 +1,5 @@ import Testing @testable import EcgSynKit -@testable import KissFFT @Test func fftTest () { // Write your test here and use APIs like `#expect(...)` to check expected conditions.