Ruby - Digital radio link (video, data, control) for airplanes, drones and UAVs

Ruby Development/Customizations/Extensions


We are actively adding new functionalities to Ruby and improving existing ones, but, if our priorities do not match yours or you need something new or something changed, new functionalities can be added to Ruby or existing ones can be changed to match one's needs. There are two ways to customize and extend the Ruby platform:
  • Using the public SDKs: Anyone can use the public SDKs to add new functionalities to Ruby. Those new functionalities can be kept private or shared with the community as the developer of such functionalities wishes to do. Read on to see how to develop using the public SDKs.

  • On Demand development: Contact us if you need new functionalities or customizations to existing ones or (re)branding and you'd like us to develop them for you. We, as developers of Ruby, have the best knowledge on how to improve, expand or customize anything on the Ruby platform. We have a portofolio of very satisfied, well known customers in the FPV/UAV world that can attest to that. Our development rates are very competitive and accessible to a broad range of businesses and individuals (it's well below 100 USD / hour).

Ruby Development SDKs


Ruby does support development of additional components by 3rd parties, on top of the core functionality.
You can see a list of available plugins that you can use, here: Plugins Library

Ruby exposes C/C++ language SKDs that anyone can use to develop and add custom components.
Below you'll find information on how to get the SDKs, how to use them and deploy custom components to Ruby.

OSD/Instruments Plugins SDK:


You can write your own OSD/Instruments plugins in order to have different OSD elements, instruments or gauges displayed on the OSD.
The plugins you write can be distributed to anyone and they can install them directly from a USB stick.
To develop an OSD/Instruments plugin, you need to follow these simple steps:

1. Download the public SDK
Download the files you'll need for writing a plugin from here;
These are just some header files in C/C++ that define all the data structures you will need for your plugin.

2. Write a C/C++ library for the plugin
The plugins are just simple linux shared libraries. They just contain a predefined list of functions that needs to be exported as C functions and then Ruby can use them.
The minimum list of functions (4 functions) your plugin needs to export in order to work correctly in Ruby, is this:
  • void init(void* pEngine);
    This function is called first and you receive a pointer to the graphics engine. You should store this and use it in the render function to draw on screen. See the SDK header files for the list of graphics functions you can use.

  • char* getName();
    This should just return a string that is the name you wish to apear in the user interface and menus.

  • char* getUID();
    This should return a unique string that differs from any other plugin. Just use random big number/string, like "75432-324-SFJKET-2342".

  • void render(vehicle_and_telemetry_info_t* pTelemetryInfo, plugin_settings_info_t* pCurrentSettings, float xPos, float yPos, float fWidth, float fHeight);
    This method will be called by Ruby to actually render your plugin on the screen. You'll receive as parameters all the info you need, like telemetry info, link info and so on (see the SDK header files for a full list of parameters you get). Keep in mind that this data is read only and gets destroyed after the call finishes. You receive updated values on the next call. If you need to save any values, you need to save them to your own variables. Your plugin should draw only in the allocated rectagle on the screen, as is defined in the parameters you get in this call. All screen coordonates in Ruby are real numbers between 0 and 1, the upper left corner of the screen is (0,0) and the bottom right corner of the screen is (1,1).


3. Compile your plugin
Assuming you wrote your 4 functions in a cpp file, let's call it my_plugin_source.cpp, now you can compile your plugin. To do so, use a make file, create one called Makefile and add this content to it:

LDFLAGS=-lrt -lpcap -lpthread
CPPFLAGS=-Wall -O0 -D _GNU_SOURCE
CFLAGS := $(CFLAGS)

all: my_plugin

%.o: %.cpp
g++ $(CFLAGS) -c -o $@ $< $(CPPFLAGS)

my_plugin: my_plugin_source.o
gcc my_plugin_source.o -shared -Wl,-soname,my_plugin.so.1 -o my_plugin.so.1.0.1 -lc
clean:
rm -f my_plugin.so.* *.o
Download the sample Makefile


Ofcourse, you should use whatever filename you need for the source file and a different name for the plugin library.
Now, assuming you have all the files (SDK header files, my_plugin.cpp and Makefile) in a folder, just type make all and you should obtain a file named my_plugin.so.1.0.1
That is your final plugin and is what you can distribute to be installed and used by anyone.
Note: If you need additional icons or images for your plugin, you need to distribute those too (Ruby knows how to install those too automatically);
Note 2: You should compile your plugin on a Raspberry Pi. It might work also on a regular Unix machine, but have not tried it.

4. Install the plugin
This is the last step and is one that can be done by anyone that has your plugin, so that they can use it.

A. Copy your plugin (and any images you might need, if any) to a USB memory stick and plug it into any USB port on the Ruby controller;
B. On Ruby controller, go to MenuController SettingsManage Plugins
C. Use the option to install additional OSD plugins. Ruby will search the memory stick and install the plugin (along with any images it finds);

How to import plugins in Ruby


That's all, you can now remove the memory stick. Your plugin should show up now on the OSD and in menus and you can configure it (move it arround, resize it) and add it to the OSD layouts you want.

Example:
Here is an example plugin, it will draw a circle and show the vehicle heading centered on the plugin viewport.


#include <stdio.h>
#include <math.h>
#include "render_engine_ui.h"
#include "telemetry_info.h"
#include "settings_info.h"

PLUGIN_VAR RenderEngineUI* g_pEngine = NULL;
PLUGIN_VAR const char* g_szPluginName = "My Example Plugin";
PLUGIN_VAR const char* g_szUID = "542AST-G3432Q-WE19J-0024";

#ifdef __cplusplus
extern "C" {
#endif

void init(void* pEngine)
{
  g_pEngine = (RenderEngineUI*)pEngine;
}

char* getName()
{
  return (char*)g_szPluginName;
}

char* getUID()
{
  return (char*)g_szUID;
}

void render(vehicle_and_telemetry_info_t* pTelemetryInfo, plugin_settings_info_t* pCurrentSettings, float xPos, float yPos, float fWidth, float fHeight)
{
  if ( NULL == g_pEngine || NULL == pTelemetryInfo )
  return;

  float xCenter = xPos + 0.5*fWidth;
  float yCenter = yPos + 0.5*fHeight;

  g_pEngine->setColors(g_pEngine->getColorOSDInstruments());
  g_pEngine->drawCircle(xCenter, yCenter, fWidth * 0.5);

  char szBuff[64];
  sprintf(szBuff, "%d", pTelemetryInfo->heading);

  u32 fontId = g_pEngine->getFontIdRegular();
  float height_text = g_pEngine->textHeight(fontId);
  float width_text = g_pEngine->textWidth(fontId, szBuff);

  g_pEngine->setColors(g_pEngine->getColorOSDText());
  g_pEngine->drawText(xCenter - 0.5 * width_text, yCenter - 0.5 * height_text, fontId, szBuff);
}

#ifdef __cplusplus
}
#endif
Download the sample Makefile
Download the sample source code

Note: The global variables you declare need to have the PLUGIN_VAR prefix so that they do not interfere with other plugins.
Note2: Remember, the size and position of the plugin on the screen is decided by the user. The user can move it arround or resize it as he needs, so it's important that you draw your plugin only in the assignated rectangle and that your plugin can resize it's drawing to fit the assignated rectangle as best as possible.

Optional functionalities and settings:

A plugin can have additional functions that can be exported and Ruby uses them for additional functionalities such as versioning and having custom plugin settings in the UI, depending on what the plugin needs (See the Ruby AHI built-in plugin for an example in the UI).
The list of optional functions a plugin can export is as follows:

  • int getVersion();
    This function should return the version of the plugin. It's used to figure out what version of a plugin the user has.

  • int getPluginSettingsCount();
    If your plugin needs custom persistent settings in the UI, that the user should be able to change, then this function should return the number of settings your plugin needs in the UI (they will be changeable by user and persistent)

  • char* getPluginSettingName(int settingIndex);
    Should return the setting name, it will show as such in the Ruby menus. Index is from 0 to (settings count - 1)

  • int getPluginSettingType(int settingIndex);
    This method should return what type a particular setting is. Currenty supported are bools, integers and enumerations (like: red, blue, green, black) (see the SDK header files for the constants that represent those types).

  • int getPluginSettingMinValue(int settingIndex);
    What is the minimum value the user should be able to set for a particular setting. Used only for integer type of settings.

  • int getPluginSettingMaxValue(int settingIndex);
    What is the maximum value the user should be able to set for a particular setting. Used only for integer type of settings.

  • int getPluginSettingDefaultValue(int settingIndex);
    What should be the default value for a particular setting, after the plugin is installed. The user can of course change the value of a setting from the menus. Boolean settings look for a 0 or 1 as default value and enumeration setting type looks for a value from 0 to enum count-1.

  • int getPluginSettingOptionsCount(int settingIndex);
    For a setting that is defined as a enumeration, tells how many options this particular setting should have (i.e. 3 for an enum with 3 options: "apples", "oranges", "cherry").

  • char* getPluginSettingOptionName(int settingIndex, int optionIndex);
    Should return the option name for a particular enumeration value in a setting of type enum, it will show in menus as such.

  • float getDefaultWidth();
    This is the default width a plugin will have after instalation. The user can move and resize the plugin after instalation. If this function is not implemented by the plugin, Ruby will choose a default width. All sizes are in (0...1) range.

  • float getDefaultHeight();
    This is the default height a plugin will have after instalation. The user can move and resize the plugin after instalation. If this function is not implemented by the plugin, Ruby will choose a default height. All sizes are in (0...1) range.

  • Note: You don't need to add settings for the plugin size and position on the screen. This is done automatically by Ruby.

    You can now change the plugin setting directly from Ruby menu:

    How to change plugin settings


    Contribute to community:
    If you write a cool plugin and you want to share it with the community, drop me a message and I can add it to the public list of plugins.