LSL Protocol/OpenRadioCommunication

From Second Life Wiki
< LSL Protocol
Revision as of 18:50, 15 September 2017 by Kyrah Abattoir (talk | contribs) (Fixes a typo in the ASCII conversion table.)
Jump to navigation Jump to search

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

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.

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
	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);
}
string ReceiverReadASCIIChar(string sDatastream)
{
	return ASCII2Char(ReceiverReadInt(sDatastream,3));
}
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;
}

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;
}

//////////////////////////////////////////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);
}

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

//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 send a morse word on the datastream,this start with 000 and end with 000
//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);
}

//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 = "";
}


//////////////////////////////////////////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);
}

integer MORSE_SPACE = 1;
integer MORSE_SHORT = 2;
integer MORSE_LONG  = 3;

list MORSE_SYMBOLS = [""," ",".","-"];
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.
    }
}