I have done some modifications to the Ghpsdr2 code such that it runs correctly on the Mac. Note that these changes have only been tested on Snow Leopard on 64-bit Intel. I make no claims about it working on any other platform. To build this version, you will need all of the GTK libraries and such described on the Ghpsdr page.
You can get the source to mac-ghpsdr from SVN. The SVN address is:
It should be in a complilable state at any particular point in time, but do realize this is my development tree. Again, it is in development, so I don't really know how things work outside of my little sandbox.
Note that you'll need my OzyUtils-MacOS to load the Ozy firmware and such. You'll need to execute this by hand before you run ghpsdr. They are available from my SVN at:
How To Compile
You must first compile the DttSP library. Do the following:
cp libDttSP.a ..
You can then type a
make as normal and run the resulting executable.
Changes Made Against the Linux Version
- Implemented the USB layer in the native Mac IOKit rather than libusb.
- Used the Asynchronous USB reads and writes so that the program doesn't wait for writes to happen. This reduces CPU load.
- Changed the code so that we read 16 "Ozy Block" of 512 bytes per read. This reduces the number of kernel calls we do, and significantly reduces CPU load. 16 seems to be the "sweet spot" because ghpsdr sends 1024 samples at a time to DttSP. 16 "Ozy Blocks" is equivalent to 16 * 63 samples = 1008. This makes it so that a single read is equal to almost a dispatch to DttSP. Empirically this seems to work well as well. If you would like to play, there is a OZY_PACKETS_PER_INPUT_BUFFER macro in ozy_buffers.h that will change the value. Make sure you run a make clean before you rebuild after modifying this file (the dependencies aren't really there in the Makefile yet). Notice that the FIFO on Ozy is 4096 words (4 bytes per word), so that means that 32 "Ozy Blocks" is the max it can hold. You shouldn't use a value above this probably.
- Changed the thread priorities such that the callback thread and the thread that empties the read buffer are real-time mach threads.
- Changed the thread priorities in the DttSP process_samples code to realtime.
- Changed the semaphores in both DttSP and ghpsdr to use the mach native calls. This seems to perform much better than the corresponding pthread calls.
- "Chained" the asynchronous writes to Ozy. When the write calls the callback function, we immediately check to see if there's enough data in the ring buffer to send another buffer sized packet to Ozy. If there is, we go ahead and immediately queue up another asynchronous write. If not, we remove the lock to signify that we're done working, and let the read chain throw out another write when there's enough data.
- "Chained" the asynchronous USB reads so that the next read is kicked off by the callback function immediately. This means that we kick off a read even before we've really dealt with the previous one. This makes sure we have at least one read "in the pipe" at any one point in time, which should hopefully keep us from being data starved.
Hints and Kinks
Make sure that when you compile FFTW that you include the --enable-sse flag in your configure command. This will make GCC emit SSE instructions for the fast Fourier transforms. This can make a huge difference in the time taken by the FFT calls.
--NH6Z 02:13, 7 February 2010 (UTC)