[GreenKeys] Precise Baud Timing

Zach Tumbusch zach.tumbusch at gmail.com
Wed Nov 12 15:09:07 EST 2025


I went through a few iterations testing with Node, PHP, and C# but settled
on this example. The bigger challenge was sorting the popping on the fast
frequency transition. I tested this up to 9600 baud with my ST-8000. The
bit stream is 8 bit characters  "A": "01100011" 11000 with 1 start bit and
2 stop bits. The actual bitrate at 45.45 baud is around 22ms. but since I
am directly converting points to a wav file I keep track of that based on
how many samples. Prints well on all my machines. Model 28 / UGC-136BX /
Hal. See the working example. https://w8zjt.net/itty

73, de W8ZJT

    async generateAFSKWav(bitstream: string, baud, mark, space): Promise<
Blob> {
        const sampleRate = 16000;  // Sample rate in Hz
        const fadeDuration = 50;   // Number of samples for smooth
frequency transition
        const samplesPerBit = Math.round(sampleRate / baud);
        const totalSamples = bitstream.length * samplesPerBit;
        let audioSamples: Float32Array = new Float32Array(totalSamples);
        let phase = 0.0;
        let currentFreq = bitstream[0] === "1" ? mark : space;
        let currentPhaseIncrement = (2 * Math.PI * currentFreq) / sampleRate
;

        for (let bitIndex = 0; bitIndex < bitstream.length; bitIndex++) {
            const newFreq = bitstream[bitIndex] === "1" ? mark : space;
            const newPhaseIncrement = (2 * Math.PI * newFreq) / sampleRate;

            for (let i = 0; i < samplesPerBit; i++) {
                if (i < fadeDuration) {
                    // Smooth frequency transition (interpolation)
                    const fadeFactor = i / fadeDuration;
                    const interpolatedFreq = currentFreq * (1 - fadeFactor)
+ newFreq * fadeFactor;
                    currentPhaseIncrement = (2 * Math.PI * interpolatedFreq)
/ sampleRate;
                } else {
                    currentPhaseIncrement = newPhaseIncrement;
                }

                // Generate waveform sample
                audioSamples[bitIndex * samplesPerBit + i] = Math.sin(phase
);
                phase += currentPhaseIncrement;

                if (phase >= 2 * Math.PI) {
                    phase -= 2 * Math.PI;
                }
            }

            currentFreq = newFreq;
        }
        // Convert to WAV format
        const wavBuffer = await WavEncoder.encode({
            sampleRate: sampleRate,
            channelData: [audioSamples] // Mono channel
        });

On Wed, Nov 12, 2025 at 1:57 PM Steve <zarco at sonic.net> wrote:

> Much easier to do in hardware with just a few chips.
> 555 timer and shift register. A couple more chips and
> send R-Y test. You can use a scope or period counter to
> accurately setup the 555 clock. Can be implemented in Baudot
> or ASCII.
>
> With a uP, use the programmable timer which in most these
> days is internal. This should be largely independent of the
> code if done right.
>
> Projects like this are fun...enjoy!
> Steve W6SSP
> ______________________________________________________________
> GreenKeys mailing list
> Home: http://mailman.qth.net/mailman/listinfo/greenkeys
> Help: http://mailman.qth.net/mmfaq.htm
> Post: mailto:GreenKeys at mailman.qth.net
>
> >>> Jordan Spencer Cunningham's GreenKeys Search Tool:
> https://teletype.net/gksearch
> >>> 2002-to-present greenkeys archive:
> http://mailman.qth.net/pipermail/greenkeys/
> >>> 1998-to-2001 greenkeys archive:
> http://mailman.qth.net/archive/greenkeys/greenkeys.html
> >>> Randy Guttery's 2001-to-2009 GreenKeys Search Tool:
> http://comcents.com/tty/greenkeyssearch.html
>
> This list hosted by: http://www.qsl.net
> Please help support this email list: http://www.qsl.net/donate.html
> Message delivered to zach.tumbusch at gmail.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.qth.net/pipermail/greenkeys/attachments/20251112/de1820a6/attachment.html>


More information about the GreenKeys mailing list