LSL Protocol/OpenRadioCommunication

From Second Life Wiki
< LSL Protocol
Revision as of 06:02, 6 August 2019 by Kyrah Abattoir (talk | contribs) (Reformatted and cleaned up the code to make it a bit less intimidating.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Open Radio Communication (ORC for short) Is a "binary like" radio system designed for small simple applications, part of the goal is to have equipments interfere with eachothers in fun and random ways.

Channels

The channels used by ORC go from -797386 to -797186 with 200 independent frequencies.

Frequency "friendly" numbers go from 88.1 to 108.1.

CORE API

//Open Radio System v0.1

integer POWER_LOW 	= 0;//whisper
integer POWER_MED 	= 1;//say
integer POWER_HI 	= 2;//shout
integer POWER_MAX 	= 3;//region

integer CHANNEL = -798267; //ORC in decimal basically
integer giSubchannel;
//This function is used to trim/pad data to length
string Size(integer iData,integer iBytes)
{
    string s = (string)iData;

    while(llStringLength(s)<iBytes)
        s = "0"+s;

    if(llStringLength(s)>iBytes)
        s = llGetSubString(s,-iBytes,-1);

    return s;
}

//////////////////////////////////////////RECEIVER//////////////////////////////////////////
//Functions to listen and read an OpenRC datastream										  //
//////////////////////////////////////////RECEIVER//////////////////////////////////////////
integer giReceiverPointer;
integer giReceiverHandle = -1;
integer Receiver(float fFrequency)
{
    integer iFrequency = llAbs((integer)(fFrequency*10));

    //set frequency to zero to turn receiver off.
    if(iFrequency == 0)
    {
        if(giReceiverHandle != -1)
	    llListenRemove(giReceiverHandle);

        giReceiverHandle = -1;
        return -1;
    }

    // 88.1 to 108.1 expressed as 881 to 1081 -> 200 possible frequencies
    if(iFrequency < 881)
        iFrequency = 881;
    else if(iFrequency > 1081)
        iFrequency = 1081;

    if(giReceiverHandle != -1)
        llListenRemove(giReceiverHandle);

    giSubchannel = CHANNEL+iFrequency;
    giReceiverHandle = llListen(giSubchannel,"","","");

    return iFrequency;
}

ReceiverInit()
{
	//init basically set the pointer to 0, always call this before you begin reading a message.
	giReceiverPointer = 0;
}

string ReceiverRead(string sDatastream,integer iBytes)
{
	//Main stream reader, read the number of bytes and move the pointer forward.
	string sData = llGetSubString(sDatastream,giReceiverPointer,giReceiverPointer + iBytes - 1);
	giReceiverPointer+=iBytes;
	return sData;
}

integer ReceiverReadInt(string sDatastream,integer iBytes)
{
	return (integer)ReceiverRead(sDatastream,iBytes);
}

//////////////////////////////////////////SENDER//////////////////////////////////////////
//Functions to build and send an OpenRCsend datastream									//
//////////////////////////////////////////SENDER//////////////////////////////////////////
string gsSendBuffer;

//Execute before building a datastream to ensure that the send buffer is clean.
SendInit()
{
	gsSendBuffer = "";
}

//Used to add an integer to the datastream, only positive values, you can pick the number of bytes used
SendInt(integer iValue,integer iBytes)
{
	gsSendBuffer += Size(llAbs(iValue),iBytes);
}

//Send the buffer and purge it's content, you have to set power and frequency here.
SendEnd(float fFrequency,integer iPower)
{
	integer iChannel = llAbs((integer)(fFrequency*10));
	// 88.1 to 108.1 expressed as 881 to 1081 -> 200 possible frequencies
	if(iChannel < 881)
		iChannel = 881;
	else if(iChannel > 1081)
		iChannel = 1081;

	iChannel += CHANNEL;
	if(gsSendBuffer == "")
		return;
	if(iPower == POWER_LOW)
		llWhisper(iChannel,gsSendBuffer);
	else if(iPower == POWER_HI)
		llShout(iChannel,gsSendBuffer);
	else if(iPower == POWER_MAX)
		llRegionSay(iChannel,gsSendBuffer);
	else
		llSay(iChannel,gsSendBuffer);
	gsSendBuffer = "";
}

ASCII TEXT ADDON

//Read an ASCII "Word" that has been encoded by SendASCIIWord()
string ReceiverReadASCIIWord(string sDatastream)
{
	//first we attempt to read the header (charcount)
	integer iLength = ReceiverReadInt(sDatastream,3);
	if(iLength < 1)
		iLength = 0;
	string sBuffer = "";
	integer i;
	for(i=0;i<iLength;i++)
		sBuffer += ReceiverReadASCIIChar(sDatastream);
	return sBuffer;
}

//Read a single ascii character (3bytes)
string ReceiverReadASCIIChar(string sDatastream)
{
	return ASCII2Char(ReceiverReadInt(sDatastream,3));
}


//Used to add an ASCII word to the datastream, words use 3 bytes per character and 3 extra bytes as header.
SendASCIIWord(string sWord)
{
	//count the number of chars
	integer iCount = llStringLength(sWord) % 999; //max word size is 999 chars because 3 bytes max.
	integer i;
	SendInt(iCount,3);	//header of a word is it's total character count.
	for(i=0;i<iCount;i++)
		SendASCIIChar(llGetSubString(sWord,i,i));
}

//Used to add an ASCII character to the datastream, each ascii char always use 3 bytes.
SendASCIIChar(string sChar)
{
	gsSendBuffer += Char2ASCII(sChar);
}

//////////////////////////////////////////CODEC//////////////////////////////////////////
//this is a basic ASCII coder/decoder toolset for char/word							   //
//////////////////////////////////////////CODEC//////////////////////////////////////////
//Conversion table to extended ascii, character index + 1 = value.
//                  1        10        20        30         40        50        60        70        80        90        100       110       120       130       140       150       160       170       180       190       200       210       220       230       240       250  255 (nbsp)
string ASCII = "☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐└┴┬├─┼ãÃ╚╔╩╦╠═╬¤ðÐÊËÈıÍÎÏ┘┌█▄¦Ì▀ÓßÔÒõÕµþÞÚÛÙýݯ´­±‗¾¶§÷¸°¨·¹³²■ ";
//Turns the supplied character into a 3 byte integer.
string Char2ASCII(string sData)
{
	return Size(llSubStringIndex(ASCII,llGetSubString(sData,0,0)) + 1,3);
}

//Turns the supplied integer into it's corresponding character. values wrap around at ORC_ASCII length.
string ASCII2Char(integer iCharcode)
{
	iCharcode = llAbs(iCharcode) % (llStringLength(ASCII)+1);
	if(iCharcode <= 0)
		return "";
	iCharcode--;	//shifting of 1 to account for the "" that is at position zero.
	return llGetSubString(ASCII,iCharcode,iCharcode);
}

MORSE CODE ADDON

integer MORSE_SPACE = 1;
integer MORSE_SHORT = 2;
integer MORSE_LONG  = 3;
list MORSE_SYMBOLS = [""," ",".","-"];

//Used to send a morse word on the datastream,it is delimited by "000" at each end
//requires pre encoded morse!
SendMorseWord(string sWord)
{
    //count the number of chars
    integer iCount = llStringLength(sWord);
    integer i;
    SendInt(0,3);
    for(i=0;i<iCount;i++)
        SendInt((integer)llGetSubString(sWord,i,i),1);
    SendInt(0,3);
}

string ReceiverReadMorseWord(string sDatastream)
{
	//you should check the header manually! (000)
	string sBuffer = "";
	integer iInt;
    do
    {
        iInt = ReceiverReadInt(sDatastream,1);
        if(iInt)
            sBuffer += (string)iInt;
    }
    while(iInt);
    
    return sBuffer;
}

//Converts a morse formatted "string" into a valid radio message buffer
string String2Morse(string sData)
{
    string buffer;
    integer i;
    integer max = llStringLength(sData);

    for(i=0;i<max;i++)
    {
        integer value = llListFindList(MORSE_SYMBOLS,[llGetSubString(sData,i,i)]);
        if(value < 0)
            value = 0;
            buffer += (string)value;
    }

    return buffer;
}


string Morse2String(string sData)
{
    string buffer;
    integer i;
    integer max = llStringLength(sData);
    for(i=0;i<max;i++)
        buffer += llList2String(MORSE_SYMBOLS,(integer)llGetSubString(sData,i,i));

    return buffer;
}

Examples

Sending a simple message with the API

Note that in this example, we are sending int(5) and then a variable length ascii encoded text. It will have to be decoded in the same order to be reserved.

state_entry()
{
    SendInit(); //Required before any transmission to setup the radio systems.
    SendInt(5000,5); //This will add a 5 digit long integer, containing the value 5000 to the buffer.
    SendASCIIWord("Hello world!"); //This will add an ASCII encoded string of variable length to the sending buffer.
    SendEnd(105.5,POWER_MED); //This will send the buffer over channel 105.5 with the "medium" power setting (20 meters range)
}

Receiving a messages with the API

The way variable length ascii encoded texts are stored, the first int(3) contains the length of the character chain, and then every subsequenct in(3) is a character.

state_entry()
{
    Receiver(105.5); //Will start listening for messages on channel 105.5
}
listen(integer channel,string name,key id,string datastream)
{
    if(channel = giSubchannel && giReceiverHandle != -1)
    {
        ReceiverInit(); //setup the ORC system for reading the datastream
        
        integer value = ReceiverReadInt(datastream,5); //read an Integer value of 5 digits.
        string text = ReceiverReadASCIIWord(datastream); //read a variable length Ascii word.
    }
}