summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArturs Artamonovs <arturs.artamonovs@protonmail.com>2025-01-06 12:30:49 +0000
committerArturs Artamonovs <arturs.artamonovs@protonmail.com>2025-01-06 12:30:49 +0000
commita0d12ecbac8fe327d1dcd4580fee594e24d4191b (patch)
tree9a7cef9ed0de080bb8c19912f678497a4fca6a86
parentf923a3824561c6cf200c638e3d44d1cbf4adf7d8 (diff)
downloadPrySDR-a0d12ecbac8fe327d1dcd4580fee594e24d4191b.tar.gz
PrySDR-a0d12ecbac8fe327d1dcd4580fee594e24d4191b.zip
Waterfall: UI drawing from filemain
-rw-r--r--PrySDR.xcodeproj/project.pbxproj16
-rw-r--r--PrySDR.xcodeproj/xcuserdata/jackyjack.xcuserdatad/xcschemes/xcschememanagement.plist14
-rw-r--r--Radio/Utils/WaterfallFile/NaiveFFT.swift7
-rw-r--r--Radio/Utils/WaterfallFile/NaiveFFT512.swift49
-rw-r--r--Radio/Utils/WaterfallFile/SimpleImage.swift2
-rw-r--r--Radio/Utils/WaterfallFile/main.swift84
-rw-r--r--Utils/FileReader.swift150
-rw-r--r--Utils/PathUtils.swift1
-rw-r--r--WaterfallFile_UI/FileSpectrum.swift55
-rw-r--r--WaterfallFile_UI/WaterfallFile_UIApp.swift2
10 files changed, 291 insertions, 89 deletions
diff --git a/PrySDR.xcodeproj/project.pbxproj b/PrySDR.xcodeproj/project.pbxproj
index 5d627d2..72c61bf 100644
--- a/PrySDR.xcodeproj/project.pbxproj
+++ b/PrySDR.xcodeproj/project.pbxproj
@@ -601,7 +601,7 @@
Utils/TestBladeRF/main.swift,
Utils/TestRtlSdr/main.swift,
Utils/WaterfallFile/main.swift,
- Utils/WaterfallFile/NaiveFFT.swift,
+ Utils/WaterfallFile/NaiveFFT512.swift,
Utils/WaterfallFile/SimpleImage.swift,
);
target = 8DD98C402CC592540062D678 /* PrySDR */;
@@ -804,7 +804,7 @@
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Utils/WaterfallFile/main.swift,
- Utils/WaterfallFile/NaiveFFT.swift,
+ Utils/WaterfallFile/NaiveFFT512.swift,
Utils/WaterfallFile/SimpleImage.swift,
);
target = 8D9A38762D196510009A4186 /* WaterfallFile */;
@@ -812,6 +812,7 @@
8D9A38832D19D277009A4186 /* Exceptions for "Utils" folder in "WaterfallFile" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
+ FileReader.swift,
PathUtils.swift,
);
target = 8D9A38762D196510009A4186 /* WaterfallFile */;
@@ -819,11 +820,19 @@
8DBA9F602D2928DE008ECB92 /* Exceptions for "Radio" folder in "WaterfallFile_UI" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
- Utils/WaterfallFile/NaiveFFT.swift,
+ Utils/WaterfallFile/NaiveFFT512.swift,
Utils/WaterfallFile/SimpleImage.swift,
);
target = 8D9A38452D194A3D009A4186 /* WaterfallFile_UI */;
};
+ 8DBA9F652D2B5158008ECB92 /* Exceptions for "Utils" folder in "WaterfallFile_UI" target */ = {
+ isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
+ membershipExceptions = (
+ FileReader.swift,
+ PathUtils.swift,
+ );
+ target = 8D9A38452D194A3D009A4186 /* WaterfallFile_UI */;
+ };
8DD98C7C2CC6320C0062D678 /* Exceptions for "LA" folder in "PrySDR" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
@@ -958,6 +967,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 */,
+ 8DBA9F652D2B5158008ECB92 /* Exceptions for "Utils" folder in "WaterfallFile_UI" target */,
8D9A38832D19D277009A4186 /* Exceptions for "Utils" folder in "WaterfallFile" target */,
);
path = Utils;
diff --git a/PrySDR.xcodeproj/xcuserdata/jackyjack.xcuserdatad/xcschemes/xcschememanagement.plist b/PrySDR.xcodeproj/xcuserdata/jackyjack.xcuserdatad/xcschemes/xcschememanagement.plist
deleted file mode 100644
index 617cef2..0000000
--- a/PrySDR.xcodeproj/xcuserdata/jackyjack.xcuserdatad/xcschemes/xcschememanagement.plist
+++ /dev/null
@@ -1,14 +0,0 @@
-<?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>SchemeUserState</key>
- <dict>
- <key>PrySDR.xcscheme_^#shared#^_</key>
- <dict>
- <key>orderHint</key>
- <integer>0</integer>
- </dict>
- </dict>
-</dict>
-</plist>
diff --git a/Radio/Utils/WaterfallFile/NaiveFFT.swift b/Radio/Utils/WaterfallFile/NaiveFFT.swift
deleted file mode 100644
index 79393b7..0000000
--- a/Radio/Utils/WaterfallFile/NaiveFFT.swift
+++ /dev/null
@@ -1,7 +0,0 @@
-//
-// NaiveFFT.swift
-// PrySDR
-//
-// Created by Jacky Jack on 05/01/2025.
-//
-
diff --git a/Radio/Utils/WaterfallFile/NaiveFFT512.swift b/Radio/Utils/WaterfallFile/NaiveFFT512.swift
new file mode 100644
index 0000000..b4dca7e
--- /dev/null
+++ b/Radio/Utils/WaterfallFile/NaiveFFT512.swift
@@ -0,0 +1,49 @@
+//
+// NaiveFFT.swift
+// PrySDR
+//
+// Created by Jacky Jack on 05/01/2025.
+//
+
+import Foundation
+import Accelerate
+
+//SUpports now only fixed sizes and not flexible implementation just a PoC
+class NaiveFFT512 {
+
+ static let sampleCount = 512
+ let forwardDCT = vDSP.DCT(count: sampleCount, transformType: .II)!
+
+ init() {
+
+ }
+
+ func getSampleCount() -> Int {
+ return NaiveFFT512.sampleCount
+ }
+
+ func computeLine(_ processingArray: [Int8]) -> [Float] {
+
+ var dataFloat = [Float](repeating: 0.0, count: NaiveFFT512.sampleCount)
+
+ if processingArray.count != NaiveFFT512.sampleCount {
+ print("Not supporting arrays not equail to \(NaiveFFT512.sampleCount)")
+ return []
+ }
+
+ 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)
+
+ var transform_result = forwardDCT.transform(adjusted)
+ transform_result = vDSP.absolute(transform_result)
+
+ return transform_result
+ }
+
+}
diff --git a/Radio/Utils/WaterfallFile/SimpleImage.swift b/Radio/Utils/WaterfallFile/SimpleImage.swift
index c05709f..4f1bed8 100644
--- a/Radio/Utils/WaterfallFile/SimpleImage.swift
+++ b/Radio/Utils/WaterfallFile/SimpleImage.swift
@@ -32,7 +32,7 @@ public class SimpleImage {
func drawPalletLine(line: Int, pixelLine:[Float]) {
for i in 0..<self.width {
var pixel = pixelLine[i]
- if pixel>256.0 {
+ if pixel>255.0 {
//print("values to high")
pixel = 255.0
}
diff --git a/Radio/Utils/WaterfallFile/main.swift b/Radio/Utils/WaterfallFile/main.swift
index cf72d56..039be72 100644
--- a/Radio/Utils/WaterfallFile/main.swift
+++ b/Radio/Utils/WaterfallFile/main.swift
@@ -7,84 +7,58 @@
import Foundation
import Accelerate
+import ArgumentParser
+
+//set the command line arguments
+struct CommandLineArgs: ParsableCommand {
+ @Argument var inputFile:String = ""
+ @Argument var outputFile:String = ""
+}
+
+let args = CommandLineArgs.parseOrExit()
print("Read binary file")
-let input_filename="rtlsdr_100M_m.cu8"
+let input_filename=args.inputFile
//get data from u8 file
-let dir = getCurrentExecutableDir()
-let filemgr = FileManager.default
-
-if !filemgr.fileExists(atPath: input_filename) {
- print("Cant find file \(input_filename)")
+let fileReader = FileReader()
+do {
+ try fileReader.open(filename: input_filename)
+} catch {
+ print("Cant open 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)
- }
-
+do {
+ try i8_arr = fileReader.readAll()
+} catch {
+ print("Got error \(error)")
}
-fileHandle?.closeFile()
+
+fileReader.close()
//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)
+let fft512 = NaiveFFT512()
+
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 = fft512.computeLine(processingArray)
- transform_result = forwardDCT.transform(adjusted)
- transform_result = vDSP.absolute(transform_result)
- let max = vDSP.maximum(transform_result)
+ //let max = vDSP.maximum(transform_result)
//print("max=\(max)")
//frequencyDomain.append(contentsOf: transform_result)
img.drawPalletLine(line: i, pixelLine: transform_result)
@@ -99,15 +73,9 @@ 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")
-
-
+img.saveAsJPEG(args.outputFile)
print("All computations are done")
diff --git a/Utils/FileReader.swift b/Utils/FileReader.swift
new file mode 100644
index 0000000..d4be92d
--- /dev/null
+++ b/Utils/FileReader.swift
@@ -0,0 +1,150 @@
+//
+// FileReader.swift
+// PrySDR
+//
+// Created by Jacky Jack on 06/01/2025.
+//
+
+import Foundation
+
+enum FileReaderError: Error {
+ case noFile
+ case readError
+ case noData
+ case seekError
+}
+
+class FileReader {
+
+ var filepath: String?
+ let filemgr = FileManager.default
+ var fileHandle: FileHandle?
+
+ init() {
+
+ }
+
+ func open(filename: String) throws {
+ let dir = getCurrentExecutableDir()
+
+ 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 readBuffer(_ count: Int) -> [UInt8] {
+ print("FileReader not implemented")
+ return []
+ }
+
+ func readBuffer(_ count: Int) -> [Int8] {
+ print("FileReader not implemented")
+ return []
+ }
+
+ func readBuffer(_ count: Int) -> [UInt16] {
+ print("FileReader not implemented")
+ return []
+ }
+
+ func readBuffer(_ count: Int) -> [Int16] {
+ print("FileReader not implemented")
+ return []
+ }
+
+ func readBuffer(_ count: Int) -> [Float] {
+ print("FileReader not implemented")
+ return []
+ }
+
+ func readAll() throws -> [UInt8] {
+ var ret_arr:[UInt8]? = nil
+ if let file = fileHandle {
+ do {
+ if let databuf = try file.readToEnd() {
+ //print("read \(databuf.count) \(databuf) bytes")
+ //let temp_arr = [Int8](databuf)
+ ret_arr = [UInt8](repeating: 0, count: databuf.count)
+ for i in 0..<ret_arr!.count {
+ //print(String(format:"%02X", databuf[i]),terminator: "")
+ //convert from 0..255 to -127..128, from UInt8->Int8
+ ret_arr![i] = databuf[i]
+ }
+ }
+ } catch {
+ print("ReadAll u8 error:\(error)")
+ throw FileReaderError.readError
+ }
+ }
+ if ret_arr == nil {
+ return []
+ } else {
+ return ret_arr!
+ }
+ }
+
+ func readAll() throws -> [Int8] {
+ var ret_arr:[Int8]? = nil
+ if let file = fileHandle {
+ do {
+ if let databuf = try file.readToEnd() {
+ //print("read \(databuf.count) \(databuf) bytes")
+ //let temp_arr = [Int8](databuf)
+ ret_arr = [Int8](repeating: 0, count: databuf.count)
+ for i in 0..<ret_arr!.count {
+ //print(String(format:"%02X", databuf[i]),terminator: "")
+ //convert from 0..255 to -127..128, from UInt8->Int8
+ let val = databuf[i]
+ if val <= 127 {
+ ret_arr![i] = Int8(val)-127
+ } else {
+ ret_arr![i] = Int8(val-128)
+ }
+ //print(String(format:"%02X", i8_arr![i]),terminator: "")
+ }
+ }
+ } catch {
+ print("ReadAll i8 error:\(error)")
+ throw FileReaderError.readError
+ }
+ }
+ if ret_arr == nil {
+ return []
+ } else {
+ return ret_arr!
+ }
+ }
+
+ func readAll() throws -> [UInt16] {
+ print("FileReader not implemented")
+ return []
+ }
+
+ func readAll() throws -> [Int16] {
+ print("FileReader not implemented")
+ return []
+ }
+
+ func readAll() throws -> [Float] {
+ print("FileReader not implemented")
+ return []
+ }
+
+ func seek(toFileOffset: UInt64) throws {
+ if let file = self.fileHandle {
+ file.seek(toFileOffset: toFileOffset)
+ }
+ }
+
+ func close() {
+ fileHandle?.closeFile()
+ }
+
+}
diff --git a/Utils/PathUtils.swift b/Utils/PathUtils.swift
index a82261a..164baa1 100644
--- a/Utils/PathUtils.swift
+++ b/Utils/PathUtils.swift
@@ -10,4 +10,5 @@ import Foundation
//get current run directory
func getCurrentExecutableDir() -> String {
return Process().currentDirectoryPath
+ //return FileManager.default.currentDirectoryPath
}
diff --git a/WaterfallFile_UI/FileSpectrum.swift b/WaterfallFile_UI/FileSpectrum.swift
index 6415dfa..ce64836 100644
--- a/WaterfallFile_UI/FileSpectrum.swift
+++ b/WaterfallFile_UI/FileSpectrum.swift
@@ -13,8 +13,15 @@ import CoreImage
class FileSpectrum: NSObject, ObservableObject {
- let default_width = 512
- let default_height = 512
+ static let default_width = 512
+ static let default_height = 512
+ static let defaultSleepTime:UInt32 = 1
+ var offset:Int=0
+ let fileReader = FileReader()
+ var dataBuffer:[Int8] = .init(repeating: 0, count: FileSpectrum.default_width)
+ var allData:[Int8]? = nil
+ let fft512 = NaiveFFT512()
+ var line_counter:Int = 0
let sessionQueue = DispatchQueue(label: "sessionQueue",
attributes: [],
@@ -22,7 +29,7 @@ class FileSpectrum: NSObject, ObservableObject {
@Published var outputImage = emptyCGImage
- let simpleImage = SimpleImage(width: 256, height: 256)
+ let simpleImage = SimpleImage(width: 512, height: 512)
/// A 1x1 Core Graphics image.
static var emptyCGImage: CGImage = {
@@ -40,24 +47,62 @@ class FileSpectrum: NSObject, ObservableObject {
return buffer.makeCGImage(cgImageFormat: fmt!)!
}()
+
+ init(_ input_filename: String) {
+ //get data from u8 file
+
+ do {
+ try fileReader.open(filename: input_filename)
+ } catch {
+ print("Cant open file \(input_filename)")
+ exit(0)
+ }
+ var i8_arr:[Int8]? = nil
+ do {
+ try i8_arr = fileReader.readAll()
+ allData = i8_arr
+ } catch {
+ print("Got error \(error)")
+ }
+
+ fileReader.close()
+ }
func makeSpectrogramImage() -> NSImage {
return NSImage(cgImage: FileSpectrum.emptyCGImage, size: .zero)
}
+
+ //just get data time to time from databuffer and pretend to be realtime data
+ func dataFileProcessor() {
+ //all now is about 512pixels
+ let processingSlice = allData![offset...offset+FileSpectrum.default_width-1]
+ let dataBuffer = Array(processingSlice)
+ var transform_result:[Float] = .init(repeating: 0.0, count: FileSpectrum.default_width)
+ //DispatchQueue.main.async {
+ transform_result = self.fft512.computeLine(dataBuffer)
+ self.simpleImage.drawPalletLine(line: self.line_counter, pixelLine: transform_result)
+ if self.offset+FileSpectrum.default_width < allData!.count {
+ self.offset += FileSpectrum.default_width
+ self.line_counter += 1
+ }
+ //}
+
+ }
func startRunning() {
sessionQueue.async {
print("lets start the task for spectrum analysis")
- for i in 0..<100 {
+ for i in 0..<512 {
print("Process the task \(i)")
self.simpleImage.drawPixel(i, i, PixelData(a: 255, r: 255, g: 0, b: 0))
DispatchQueue.main.async {
+ self.dataFileProcessor()
self.outputImage = self.simpleImage.toCGImage()!
}
- sleep(1)
+ sleep(FileSpectrum.defaultSleepTime)
}
}
}
diff --git a/WaterfallFile_UI/WaterfallFile_UIApp.swift b/WaterfallFile_UI/WaterfallFile_UIApp.swift
index 959fe55..be9f25a 100644
--- a/WaterfallFile_UI/WaterfallFile_UIApp.swift
+++ b/WaterfallFile_UI/WaterfallFile_UIApp.swift
@@ -11,7 +11,7 @@ import SwiftData
@main
struct WaterfallFile_UIApp: App {
- let fileSpectrum = FileSpectrum()
+ let fileSpectrum = FileSpectrum("rtlsdr_99.5M_m.cu8")
@Environment(\.scenePhase) private var scenePhase