diff options
author | Arturs Artamonovs <arturs.artamonovs@protonmail.com> | 2025-01-05 10:07:35 +0000 |
---|---|---|
committer | Arturs Artamonovs <arturs.artamonovs@protonmail.com> | 2025-01-05 10:07:35 +0000 |
commit | f923a3824561c6cf200c638e3d44d1cbf4adf7d8 (patch) | |
tree | cdf4d935e8be653a7d296d4cd48e06cffd3b376d /Radio/Utils/WaterfallFile | |
parent | b999f85d83728bd7034e85f2e038bb9a6454b16a (diff) | |
download | PrySDR-f923a3824561c6cf200c638e3d44d1cbf4adf7d8.tar.gz PrySDR-f923a3824561c6cf200c638e3d44d1cbf4adf7d8.zip |
Waterfall: drawing from file initial implementatiom, visual version concept is ready
Diffstat (limited to 'Radio/Utils/WaterfallFile')
-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 |
3 files changed, 236 insertions, 0 deletions
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") |