Skip to content

Commit 060cab7

Browse files
committed
Downsampler now utilizes a new downsampleReal function, will hopefully run in macOS 13.
1 parent 24e612f commit 060cab7

2 files changed

Lines changed: 43 additions & 20 deletions

File tree

Sources/SignalTools/Downsample.swift

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,10 @@ public class Downsampler {
117117

118118
}
119119

120-
121120
public func downsampleComplex(iqData: [DSPComplex], decimationFactor: Int, filter: [Float] = [0.5, 0.5]) -> [DSPComplex] {
122121
guard iqData.count > (filter.count - 1) else { // Less data than is needed to apply the filter, thus no output.
123122
return []
124123
}
125-
let usableSamplesCount = iqData.count - (filter.count - 1) // Samples with enough proceeding samples to apply the filter.
126-
// let outputCount = max(usableSamplesCount / decimationFactor, 1)
127124

128125
var returnVector: [DSPComplex]
129126
var splitComplexData = DSPSplitComplex(realp: .allocate(capacity: iqData.count), imagp: .allocate(capacity: iqData.count))
@@ -134,8 +131,10 @@ public func downsampleComplex(iqData: [DSPComplex], decimationFactor: Int, filte
134131
vDSP.convert(interleavedComplexVector: iqData, toSplitComplexVector: &splitComplexData)
135132
let iBranchBufferPointer = UnsafeBufferPointer(start: splitComplexData.realp, count: iqData.count)
136133
let qBranchBufferPointer = UnsafeBufferPointer(start: splitComplexData.imagp, count: iqData.count)
137-
var iBranchDownsampled = vDSP.downsample(iBranchBufferPointer, decimationFactor: decimationFactor, filter: filter)
138-
var qBranchDownsampled = vDSP.downsample(qBranchBufferPointer, decimationFactor: decimationFactor, filter: filter)
134+
let iBranchArray: [Float] = Array(iBranchBufferPointer)
135+
let qBranchArray: [Float] = Array(qBranchBufferPointer)
136+
var iBranchDownsampled = downsampleReal(data: iBranchArray, decimationFactor: decimationFactor, filter: filter)
137+
var qBranchDownsampled = downsampleReal(data: qBranchArray, decimationFactor: decimationFactor, filter: filter)
139138
returnVector = .init(repeating: DSPComplex(real: 0, imag: 0), count: iBranchDownsampled.count)
140139
return iBranchDownsampled.withUnsafeMutableBufferPointer { iDownsampledBufferPointer in
141140
qBranchDownsampled.withUnsafeMutableBufferPointer { qDownsampledBufferPointer in
@@ -147,11 +146,8 @@ public func downsampleComplex(iqData: [DSPComplex], decimationFactor: Int, filte
147146
}
148147

149148
public func downsampleReal(data: [Float], decimationFactor: Int, filter: [Float] = [0.5, 0.5]) -> [Float] {
150-
return vDSP.downsample(data, decimationFactor: decimationFactor, filter: filter)
151-
}
152-
153-
public func downsampleRealX(data: [Float], decimationFactor: Int, filter: [Float] = [0.5, 0.5]) -> [Float] {
154-
let outputCount = data.count / decimationFactor
149+
let usableSampleCount = data.count - (filter.count - 1)
150+
let outputCount = Int(ceil(Double(usableSampleCount) / Double(decimationFactor)))
155151
var outputBuffer: [Float] = .init(repeating: 0.0, count: outputCount)
156152
var dataMutableCopy: [Float] = data
157153
var filterMutableCopy: [Float] = filter
@@ -160,3 +156,35 @@ public func downsampleRealX(data: [Float], decimationFactor: Int, filter: [Float
160156

161157
return outputBuffer
162158
}
159+
160+
public func downsampleComplexOLD(iqData: [DSPComplex], decimationFactor: Int, filter: [Float] = [0.5, 0.5]) -> [DSPComplex] {
161+
guard iqData.count > (filter.count - 1) else { // Less data than is needed to apply the filter, thus no output.
162+
return []
163+
}
164+
// let usableSamplesCount = iqData.count - (filter.count - 1) // Samples with enough proceeding samples to apply the filter.
165+
// let outputCount = max(usableSamplesCount / decimationFactor, 1)
166+
167+
var returnVector: [DSPComplex]
168+
var splitComplexData = DSPSplitComplex(realp: .allocate(capacity: iqData.count), imagp: .allocate(capacity: iqData.count))
169+
defer {
170+
splitComplexData.realp.deallocate()
171+
splitComplexData.imagp.deallocate()
172+
}
173+
vDSP.convert(interleavedComplexVector: iqData, toSplitComplexVector: &splitComplexData)
174+
let iBranchBufferPointer = UnsafeBufferPointer(start: splitComplexData.realp, count: iqData.count)
175+
let qBranchBufferPointer = UnsafeBufferPointer(start: splitComplexData.imagp, count: iqData.count)
176+
var iBranchDownsampled = vDSP.downsample(iBranchBufferPointer, decimationFactor: decimationFactor, filter: filter)
177+
var qBranchDownsampled = vDSP.downsample(qBranchBufferPointer, decimationFactor: decimationFactor, filter: filter)
178+
returnVector = .init(repeating: DSPComplex(real: 0, imag: 0), count: iBranchDownsampled.count)
179+
return iBranchDownsampled.withUnsafeMutableBufferPointer { iDownsampledBufferPointer in
180+
qBranchDownsampled.withUnsafeMutableBufferPointer { qDownsampledBufferPointer in
181+
let splitDownsampledData = DSPSplitComplex(realp: iDownsampledBufferPointer.baseAddress!, imagp: qDownsampledBufferPointer.baseAddress!)
182+
vDSP.convert(splitComplexVector: splitDownsampledData, toInterleavedComplexVector: &returnVector)
183+
return returnVector
184+
}
185+
}
186+
}
187+
188+
public func downsampleRealOLD(data: [Float], decimationFactor: Int, filter: [Float] = [0.5, 0.5]) -> [Float] {
189+
return vDSP.downsample(data, decimationFactor: decimationFactor, filter: filter)
190+
}

Tests/SignalToolsTests/SignalToolsTests.swift

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,7 @@ class SignalToolsTests: XCTestCase {
149149
randomTapsCount = Int.random(in: 3...151) | 1
150150
print("Decimation factor: \(randomDecimationFactor) \nOutput sample rate: \(randomOutputSampleRate) \nInput sample rate: \(randomInputSampleRate) \nTaps count: \(randomTapsCount)")
151151
let testDataDownsampleFilter = try FIRFilter(type: .lowPass, cutoffFrequency: Double(Double(randomOutputSampleRate) / 2.0), sampleRate: randomInputSampleRate, tapsLength: randomTapsCount)
152-
let downsampledOriginal = SignalTools.downsampleRealX(data: testData, decimationFactor: randomDecimationFactor, filter: testDataDownsampleFilter.getTaps())
153-
return;
154-
let downsampledvDSP = vDSP.downsample(testData, decimationFactor: randomDecimationFactor, filter: testDataDownsampleFilter.getTaps())
152+
let downsampledFullPass = SignalTools.downsampleReal(data: testData, decimationFactor: randomDecimationFactor, filter: testDataDownsampleFilter.getTaps())
155153
let downsampler = Downsampler(inputSampleRate: randomInputSampleRate, outputSampleRate: randomOutputSampleRate, filter: testDataDownsampleFilter.getTaps())
156154
let randomlySplitData = randomlySplitArray(testData)
157155
var downsampledOutput: [Float] = []
@@ -160,12 +158,10 @@ class SignalToolsTests: XCTestCase {
160158
downsampledOutput.append(contentsOf: output!)
161159
}
162160

163-
XCTAssertTrue(valsAreClose(downsampledOutput, downsampledOriginal, threshold: 0.00001))
164-
XCTAssertTrue(valsAreClose(downsampledvDSP, downsampledOriginal))
161+
XCTAssertTrue(valsAreClose(downsampledOutput, downsampledFullPass, threshold: 0.00001))
165162
}
166163

167164
func testComplexDownsamplerEquivalence() throws {
168-
return
169165
let testData = randomComplexData
170166
let randomDecimationFactor = Int.random(in: 2...100)
171167
let randomOutputSampleRate = Int.random(in: 100...48000)
@@ -175,8 +171,8 @@ class SignalToolsTests: XCTestCase {
175171

176172
let testDataDownsampleFilter = try FIRFilter(type: .lowPass, cutoffFrequency: Double(Double(randomOutputSampleRate) / 2.0), sampleRate: randomInputSampleRate, tapsLength: randomTapsCount)
177173

178-
let downsampledOriginal = SignalTools.downsampleComplex(iqData: testData, decimationFactor: randomDecimationFactor, filter: testDataDownsampleFilter.getTaps())
179-
174+
// let downsampledOriginal = SignalTools.downsampleComplexOLD(iqData: testData, decimationFactor: randomDecimationFactor, filter: testDataDownsampleFilter.getTaps())
175+
let downsampledFullPass = SignalTools.downsampleComplex(iqData: testData, decimationFactor: randomDecimationFactor, filter: testDataDownsampleFilter.getTaps())
180176
let downsampler = Downsampler(inputSampleRate: randomInputSampleRate, outputSampleRate: randomOutputSampleRate, filter: testDataDownsampleFilter.getTaps())
181177
let randomlySplitData = randomlySplitArray(testData)
182178
var downsampledOutput: [DSPComplex] = []
@@ -186,9 +182,8 @@ class SignalToolsTests: XCTestCase {
186182
}
187183

188184
print(downsampledOutput.count)
189-
print(downsampledOriginal.count)
190185

191-
XCTAssertTrue(valsAreClose(downsampledOutput, downsampledOriginal, threshold: 0.001))
186+
XCTAssertTrue(valsAreClose(downsampledOutput, downsampledFullPass, threshold: 0.001))
192187
}
193188

194189
}

0 commit comments

Comments
 (0)