Difference between revisions of "LSL Protocol/OpenRadioCommunication"

From Second Life Wiki
Jump to navigation Jump to search
(Fixes a typo in the ASCII conversion table.)
(Reformatted and cleaned up the code to make it a bit less intimidating.)
 
Line 1: Line 1:
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.
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=
=Channels=
The channels used by ORC go from -797386 to -797186 with 200 independent frequencies.
The channels used by ORC go from -797386 to -797186 with 200 independent frequencies.
Line 5: Line 5:
Frequency "friendly" numbers go from 88.1 to 108.1.
Frequency "friendly" numbers go from 88.1 to 108.1.


=API=
=CORE API=
<source lang="lsl2">
<source lang="lsl2">
//Open Radio System v0.1
//Open Radio System v0.1
Line 19: Line 19:
string Size(integer iData,integer iBytes)
string Size(integer iData,integer iBytes)
{
{
string s = (string)iData;
    string s = (string)iData;
while(llStringLength(s)<iBytes)
 
s = "0"+s;
    while(llStringLength(s)<iBytes)
if(llStringLength(s)>iBytes)
        s = "0"+s;
s = llGetSubString(s,-iBytes,-1);
 
return s;
    if(llStringLength(s)>iBytes)
        s = llGetSubString(s,-iBytes,-1);
 
    return s;
}
}


Line 34: Line 37:
integer Receiver(float fFrequency)
integer Receiver(float fFrequency)
{
{
integer iFrequency = llAbs((integer)(fFrequency*10));
    integer iFrequency = llAbs((integer)(fFrequency*10));
//set frequency to zero to turn receiver off.
 
if(iFrequency == 0)
    //set frequency to zero to turn receiver off.
{
    if(iFrequency == 0)
if(giReceiverHandle != -1)
    {
llListenRemove(giReceiverHandle);
        if(giReceiverHandle != -1)
giReceiverHandle = -1;
    llListenRemove(giReceiverHandle);
return -1;
 
}
        giReceiverHandle = -1;
        return -1;
    }


// 88.1 to 108.1 expressed as 881 to 1081 -> 200 possible frequencies
    // 88.1 to 108.1 expressed as 881 to 1081 -> 200 possible frequencies
if(iFrequency < 881)
    if(iFrequency < 881)
iFrequency = 881;
        iFrequency = 881;
else if(iFrequency > 1081)
    else if(iFrequency > 1081)
iFrequency = 1081;
        iFrequency = 1081;
 
    if(giReceiverHandle != -1)
        llListenRemove(giReceiverHandle);
 
    giSubchannel = CHANNEL+iFrequency;
    giReceiverHandle = llListen(giSubchannel,"","","");


if(giReceiverHandle != -1)
    return iFrequency;
llListenRemove(giReceiverHandle);
giSubchannel = CHANNEL+iFrequency;
giReceiverHandle = llListen(giSubchannel,"","","");
return iFrequency;
}
}
ReceiverInit()
ReceiverInit()
{
{
//init basically set the pointer to 0
//init basically set the pointer to 0, always call this before you begin reading a message.
giReceiverPointer = 0;
giReceiverPointer = 0;
}
}
string ReceiverRead(string sDatastream,integer iBytes)
string ReceiverRead(string sDatastream,integer iBytes)
{
{
Line 68: Line 77:
return sData;
return sData;
}
}
integer ReceiverReadInt(string sDatastream,integer iBytes)
integer ReceiverReadInt(string sDatastream,integer iBytes)
{
{
return (integer)ReceiverRead(sDatastream,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;
}
}


Line 120: Line 98:
{
{
gsSendBuffer += Size(llAbs(iValue),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);
}
}


Line 174: Line 122:
llSay(iChannel,gsSendBuffer);
llSay(iChannel,gsSendBuffer);
gsSendBuffer = "";
gsSendBuffer = "";
}
</source>
=ASCII TEXT ADDON=
<source lang="lsl2">
//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//////////////////////////////////////////
//////////////////////////////////////////CODEC//////////////////////////////////////////
Line 198: Line 187:
return llGetSubString(ASCII,iCharcode,iCharcode);
return llGetSubString(ASCII,iCharcode,iCharcode);
}
}
</source>
=MORSE CODE ADDON=
<source lang="lsl2">


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


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 String2Morse(string sData)
{
{
string buffer;
    string buffer;
integer i;
    integer i;
integer max = llStringLength(sData);
    integer max = llStringLength(sData);
for(i=0;i<max;i++)
 
{
    for(i=0;i<max;i++)
  integer value = llListFindList(MORSE_SYMBOLS,[llGetSubString(sData,i,i)]);
    {
  if(value < 0)
        integer value = llListFindList(MORSE_SYMBOLS,[llGetSubString(sData,i,i)]);
      value = 0;
        if(value < 0)
  buffer += (string)value;
            value = 0;
}
            buffer += (string)value;
    }
 
     return buffer;
     return buffer;
}
}


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

Latest revision as of 06:02, 6 August 2019

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