The realtime part of the software is implemented in a device driver in the FreeBSD kernel. The driver will configure the ADC card and setup things so the samples are received in a ring-buffer. The card can only be instructed about one bus-master DMA operation at a time, so whenever a page has been filled, we get an interrupt where we have to set up the DMA engine for the next page before the on-card FIFO runs full.
From the ring buffer a kernel process picks up the samples and runs through the configured "waves" in sequence. A "wave" is defined by an offset, a modulus and a length using an ioctl(2) based API.
To receive the 7499 LORAN-C chain, a wave is configured with a modules and length of 74990 * 2 * 1.25 and an offset of zero. The "74990" is the GRI period of the chain. The "2" is because I want the GRI-A and GRI-B separately so I can correlate the master/slave codes in the signal. The "1.25" is the number of samples per microsecond.
If I wanted to setup a wave to only capture the GRI-A master pulse set of the 7499 chain, I would reduce the length, but not the period to 11 msec * 1250 and set the offset to match the beginning of the window.
An averaging factor is also configured, this controls the averaging period of the exponential averaging used. I have not fully explored the effect of this yet.
The device driver can accomodate 25 such waves, but the CPU speed may may limit the number that can actually be used.
The wave is stored in an array of integers which can be memory mapped by applications so we avoid tedious and CPU expensive copies from the kernel to userland.
Given the kernel support above, and a sufficiently stable timebase, the remaining software can run at whatever speed it wants in userland, and it is even possible to run multiple differnet pieces of software at the same time.
I am obviously not finished with this yet, accute observers will wonder if I ever will be finished, I will put up some descriptions when I have something which is not quite as liquid as quicksilver.