Thanks for the comment.
The code that generates the panadapter and the waterfall is in the file CIVDecode.swift starting around line 101 (case 0x27). It probably should be moved to its own file at some point.
To generate the waterfall, the program first multiplies the raw value from the radio by a gain factor (currently 4, but in the future should be user adjustable) then clips it to a max of 255. It then uses the colorMap array (see Palette.swift) to convert it to a color. The color mapping currently in use is based on the mapping from FLDigi.
While the Mac has very good drawing primitives, I couldn’t figure out how to do display a scrolling waterfall using them, so I had to manipulate memory directly. The PixelColor class creates a 32-bit value that corresponds to a RGBA pixel in a bitmap.
To start, the program creates an in-memory background bitmap 689 pixels (number of columns in the IC7610 waterfall display) wide and the 200 pixels (adjustable) high.
The program creates a 1 dimensional array of 689 pixels. It the does a memory move of the memory for the background bitmap, moving everything up in memory by 689 pixels then copies the new pixel row to the start, creating the scrolling waterfall. It then draws this bitmap on the screen every time a new row of data is received from the radio.
For the panadapter portion, the program creates an array of 689 arrays each long enough to hold the most recent X (value determined by the length of the history, lets use 40 for 10 seconds of history with 4 readings per second) readings. There is an index that cycles from 0 to 39 and back to 0 with each reading (circular buffer). Each new set of readings is written to that index in the corresponding array based on the column. So for one reading, the data is written to row 38, the next to row 39, the next to row 0. That way, this array contains the last 40 readings, reading 41 is overwritten by a new one. Originally, to get the history (the peak hold), each time there was a new reading, the program calculated the max value of each of those 689 arrays, but I tried a variation on that to speed things up. There is another 689 element array that holds the peak value for each column. If the new value being written to a column is greater than the current max for that column, the max value for that column is updated, else if the value being replaced is equal to the max value, the new max value is calculated as the max value of the column array. This way, the program is not doing a max calculation on every column with every update.
So now the program has an array of the max(hold) values and another array with the current values.
The Mac has a graphic concept of a path, which is a closed shape of lines (or curves, but the program just uses lines). It draws a path from the bottom left up to the first value, then to each of the next values then finally to the bottom right and finally back to the bottom left. It does this for both the history and current arrays. It then draws the background grid, the history path and the current path. It is able to do this on each update cycle. The path drawing and screen updating are in the file BandscopeView.swift
73,
Mark