First a bit of background...

Zock77, a frequent Forum contributor and game maker posted a question that got me interested.

http://social.msdn.microsoft.com/Forums/en-US/fdc0e238-247c-449b-a72a-05aa438ecf62/sound-outputs-and-such-with-smallbasic?forum=smallbasic

Here is a quote from Zock77's post.

" I've been trying to see if it is possible to control a RC (remote control) plane with a computer. After a bit of googleing, I found this:

https://www.insecure.ws/2010/03/09/control-rc-aircrafts-from-your-computer-for-0 "

The link explains what a PPM (Pulse Position Modulation) is and how to generate one for a specific RC device (Spektrum DX7).

This is just the kind of thing that gets me interested in a little side project.

Now the Extension...

A PPM is just a specific sound wave that is created with very specific characteristics.  Any sound wave is just a set of rapidly changing voltage levels.  Incidentally, the first extension I wrote for Small Basic was one called "SpeechMusic".  The music part was creating sounds using DirectX to manually create the waveforms, so I resurrected and modified this code.

To bring it up-to-date I used the excellent .Net library SlimDX to communicate with a DirectX Sound card.  You will need to install the SlimDX .Net 2.0 runtime.

The wave form I created consists of 192000 values per second!  Each value (converted to a voltage by the sound card) is a number ranging from -32768 to 32767 (a 16 bit or 2 byte signed number).  The maximum value is the amplitude and can be controlled by the extension to control the output voltage range.

So the program just does the simple maths to set the value for each point on the wave (192000 times per second) in an array.

The extension has a couple methods that create a Sine and Square wave for test purposes.

The business end of the extension code for these is quite simple:

case 1: //Sinusoidal
    for (int i = 0; i < sampleCount; i++)
    {
        frac = frequency * duration * i / (double)sampleCount;
        value = System.Math.Sin(2.0 * System.Math.PI * frac);
        rawsamples[i] = (short)(amplitude * value);
    }
    break;
case 2: //Square
    for (int i = 0; i < sampleCount; i++)
    {
        frac = frequency * duration * i / (double)sampleCount;
        frac = frac - (int)frac;
        value = frac < 0.5 ? -1.0 : 1.0;
        rawsamples[i] = (short)(amplitude * value);
    }
    break;

It also has a property to set the signal amplitude and a method to create a PPM for DX7.  The input is just an array of values between 0 and 1 indicating the controller position (e.g. throttle servo).  Each servo (usually 8) has a value that creates a positive voltage for a time between 0.7 and 1.5 ms followed by a 0.4 ms stop.  The total pulse only lasts 22.5 ms.

First I work out how many values for each segment of the signal are needed, then form them into an array to send to the sound card.

short[] rawsamples = new short[sampleCount];
int stopSamples = (int)(0.0004 * waveFormat.SamplesPerSecond);
List<int> servoSamples = new List<int>();
Primitive indices = SBArray.GetAllIndices(channels);
int servoCount = SBArray.GetItemCount(indices);
for (iServo = 1; iServo <= servoCount; iServo++)
{
    servoSamples.Add((int)((0.0007 + 0.0008 * channels[indices[iServo]]) * waveFormat.SamplesPerSecond));
}
//Lead-in
int leading = sampleCount - (servoCount + 1) * stopSamples - servoSamples.Sum();
int sample = 0;
for (i = 0; i < leading; i++) rawsamples[sample++] = 0;
//Servos
for (i = 0; i < stopSamples; i++) rawsamples[sample++] = (short)(-amplitude);
for (iServo = 0; iServo < servoCount; iServo++)
{
    for (i = 0; i < servoSamples[iServo]; i++) rawsamples[sample++] = amplitude;
    for (i = 0; i < stopSamples; i++) rawsamples[sample++] = (short)(-amplitude);
}

And my Small Basic test code.

While ("True")
  start = Clock.ElapsedMilliseconds  
  status = "Channels"
  For i = 1 To 8
    If LDUtilities.KeyDown("Right") And LDUtilities.KeyDown("D"+i) Then
      channel[i] = 0
    ElseIf LDUtilities.KeyDown("Left") And LDUtilities.KeyDown("D"+i) Then
      channel[i] = 1
    Else
      channel[i] = 0.5
    EndIf
    status = status + " : " + channel[i]
  EndFor
  WaveForm.PlayDX7(channel)
  GraphicsWindow.Title = status  
  delay = 50-(Clock.ElapsedMilliseconds-start)
  If (delay > 0) Then
    Program.Delay(delay)
  EndIf
EndWhile

Conclusions...

A fairly simple extension was written that can be used to Graduate from Small Basic and start leaning C#.  It can be modified to create any PPM signal which can then be controlled from Small Basic.  You could control using a GraphicsWindow interface, a joystick, game controller or pre-programmed auto-pilot etc.  It also plays havoc with my phone and may have other applications controlling devices that can accept a PPM signal.

So does it work?  Well, I didn't know when I wrote this blog, but Zock77 recently confirmed it does "Just tried it on my brothers PC and it worked BEAUTIFULLY! Controlled the plane really well!".  I just need a RC airplane now.

The full Visual Studio C# 2010 Express solution and code is available for download to play with.