Notes on the 23kbps prototype
See also #4 for more discussion.
The main test environment for 23kbps receive is sense4.s, a short 6502 assembly program that receives images over the cassette port and displays them as hi-res images (8kb each image, stored from $2000 to $3fff).
cl65 -t apple2 -C apple2-asm.cfg sense4.s ../bin/c2t-96h -2 ~/garden/apple2/sense4,bf00 sense4.wav
Then on the Apple II, follow the instructions generated by ct2:
CALL -151 BF00.BF91R BF00G
Now generate waveforms using gen3.py and send them:
python gen3.py < apple.hgr | play -r 48000 -e unsigned -b 16 -c 1 -t raw /dev/stdin
Images should appear.
An explanation of the encoding
A cycle is a high pulse followed by a low pulse. The width of each pulse carries 2 bits of information; the width is (44 + 27 * sym) µs where sym is 0 through 3. A byte is 4 symbols (in big-endian order, the top two bits are the first symbol).
There is a bit of massaging of the signal to increase the contrast between
wide and narrow pulses, to compensate for degradations in the signal path that
reduce this contrast. That logic is in
decidePulseWidthRaw and is a bit
hard to explain; all the fudge factors were determined by experiment. Without
those corrections, the encoding still kinda works but is not reliable.
The amplitude of the pulse is inversely proportional to its width, to guarantee that the generated signal has no DC offset. Any DC offset would be filtered out by the high-pass filter between the cassette port and 741 input, and would have the effect of further reducing contrast between wide and narrow pulses.
The Python waveform generating code contains an atlas of the possible pulse widths, each of which is obtained from convolving a box with an FIR filter that band-limits to the audio range. For simplicity and easy visualization of the results, internally the code generates a signal sampled at 192kHz. Downconversion to 48kHz is simple; just taking every 4th sample works because it is band-limited.
The leader is 128
3 symbols (wide pulses) followed by
[0, 3, 3, 3] repeated 4
times. This pattern is designed to be easy to detect (it's only the 14 lines
of code following
start_sync0) and hard to trigger accidentally.
The Python code was designed for experimentation, and is not a particularly efficient way to generate waveforms. A better approach is to use BLEP generation. This is a general technique that can generate all existing waveforms, with higher quality (which should manifest as a wider range of volumes for which decoding is reliable) and greater flexiblity of choice for sampling rate.