The following code snippets walk through the main aspects of setting up the Delsys API to perform an RF data collection. For more complete code, please see the relevant Delsys API sample application(s). For more information on Delsys API class and interface functionality, please see the Documentation section of this website.
Initialize Data Source and Pipeline
The Datasource will have to be called using the DeviceSourcePortable Class. In order to access the device, you will need to license your API product.
After receiving the Datasource, the following commands allow us to set up a pipeline of RF data, and access the Component and Transform Managers. Finally, we hook up to events allowing us to view:
// Load your license and key files
// This tutorial assumes you have them contained in embedded resources named PublicKey.lic and License.lic, as part of
// a solution with an output executable called BasicExample.
var assembly = Assembly.GetExecutingAssembly();
string key;
using (Stream stream = assembly.GetManifestResourceStream("BasicExample.PublicKey.lic"))
{
StreamReader sr = new StreamReader(stream);
key = sr.ReadLine();
}
string lic;
using (Stream stream = assembly.GetManifestResourceStream("BasicExample.License.lic"))
{
StreamReader sr = new StreamReader(stream);
lic = sr.ReadToEnd();
}
// The API uses a factory method to create the data source of your application.
// This creates the factory method, which will then give the data source for your platform.
// In this case the platform is RF.
var deviceSourceCreator = new DeviceSourcePortable(key, license);
// Sets the output stream for debugging information from the API. This could be a file stream,
// but in this example we simply use the Console.WriteLine output stream.
deviceSourceCreator.SetDebugOutputStream(Console.WriteLine);
// Here is where we tell the factory method what type of data source we want to receive,
// which we then set a reference to for future use.
DeviceSource = deviceSourceCreator.GetDataSource(SourceType.TRIGNO_RF);
// Here we use the key and license we previously loaded.
DeviceSource.Key = key;
DeviceSource.License = license;
// Create a reference to the first Pipeline (which was generated by the factory method above)
// for easier access to various objects within the API.
RFPipeline = PipelineController.Instance.PipelineIds[0];
TransformManager = PipelineController.Instance.PipelineIds[0].TransformManager;
// Just setting up some of the necessary callbacks from the API.
RFPipeline.CollectionStarted += CollectionStarted;
RFPipeline.CollectionDataReady += CollectionDataReady;
RFPipeline.CollectionComplete += CollectionComplete;
RFPipeline.TrignoRfManager.ComponentAdded += ComponentAdded;
RFPipeline.TrignoRfManager.ComponentLost += ComponentLost;
RFPipeline.TrignoRfManager.ComponentRemoved += ComponentRemoved;
RFPipeline.TrignoRfManager.ComponentScanComplete += ComponentScanComplete;
// The component manager is how you reference specific / individual sensors so creating
// a reference to it will shorten a lot of calls.
var ComponentManager = PipelineController.Instance.PipelineIds[0].TrignoRfManager;
Scan for Components
To detect components once the pipeline has been set up, you simply call the Scan function. This will find any sensors in range. This functionality works the same for both RF and Bluetooth.
RFPipeline.Scan();
Configure Components and Data Source
The following code demonstrates how you can select or deselect paired sensors and add a new sensor to the list. You can also select from the available sample modes for each sensor.
var inputConfiguration = new TrignoDsConfig();
// To ensure we only ever have one reference to a datasource configuration,
// you must manually remove this reference.
if (PortableIoc.Instance.CanResolve<TrignoDsConfig>())
{
PortableIoc.Instance.Unregister<TrignoDsConfig>();
}
// Here we register the new/latest one.
PortableIoc.Instance.Register(ioc => inputConfiguration);
// Now we select the default mode of each sensor that is in a viable (allocated) state.
foreach (var somecomp in RFPipeline.TrignoRfManager.Components.Where(x => x.State == SelectionState.Allocated))
{
somecomp.Configuration.SelectSampleMode(somecomp.DefaultMode);
if (somecomp.Configuration == null)
{
return false;
}
}
// Apply the input and out configurations. Generate the transforms.
PipelineController.Instance.PipelineIds[0].ApplyInputConfigurations(inputConfiguration);
var transformTopology = GenerateTransforms();
PipelineController.Instance.PipelineIds[0].ApplyOutputConfigurations(transformTopology);
// Set the run time to be max value (596,523 hours of data collection... or until you close the program.)
PipelineController.Instance.PipelineIds[0].RunTime = int.MaxValue;
Before collecting data, we configure the Datasource and hook up to the Data Events. The data ready event will allow us to view collected data.
TrignoDsConfig rfconfig = new TrignoDsConfig();
// A triggered setup could be used by setting these to true.
// The first bool is to wait for a start trigger and the second is to wait for a stop trigger.
rfconfig.ArmTrigger(false, false);
// These callbacks are where we get our data and notification of our data collection ending.
PipelineController.Instance.PipelineIds[0].CollectionDataReady += PCollectionDataReady;
PipelineController.Instance.PipelineIds[0].CollectionComplete += PCollectionComplete;
//apply the datasource configurations.
PipelineController.Instance.PipelineIds[0].ApplyInputConfigurations(rfconfig);
Configure Transforms and Arm
Finally, create and configure the desired transforms, after configuration is complete, arm the pipeline. This means that configurations have been applied and the pipeline is locked down for data collection. In order to change a configuration, the pipeline will need to be disarmed, configured, and re-armed.
//apply the transform and sensor configurations.
var outconfig = ConfigureTransforms();
PipelineController.Instance.PipelineIds[0].ApplyOutputConfigurations(outconfig);
public OutputConfig ConfigureTransforms()
{
RFPipeline.TransformManager.TransformList.Clear();
//Create the transforms for the first time.
int sensorNum = 0;
int channelNum = 0;
for (int i = 0; i < RFPipeline.TrignoRfManager.Components.Count; i++)
{
if (RFPipeline.TrignoRfManager.Components[i].State == SelectionState.Allocated)
{
var tmp = RFPipeline.TrignoRfManager.Components[i];
sensorNum++;
channelNum += tmp.TrignoChannels.Count;
}
}
if (TransformManager.TransformList.Count == 0)
{
var t = new TransformRawData(channelNum, channelNum);
TransformManager.AddTransform(t);
}
// We configure the channels each time transforms are armed.
var t0 = TransformManager.TransformList[0];
var outconfig = new OutputConfig();
outconfig.NumChannels = channelNum;
int channelIndex = 0;
for(int i = 0; i < RFPipeline.TrignoRfManager.Components.Count; i++)
{
var component = RFPipeline.TrignoRfManager.Components[i];
if (component.State == SelectionState.Allocated)
{
for (int k = 0; k < component.TrignoChannels.Count; k++)
{
var chin = component.TrignoChannels[k];
var chout = new ChannelTransform(chin.FrameInterval, chin.SamplesPerFrame, Units.VOLTS);
TransformManager.AddInputChannel(t0, chin);
TransformManager.AddOutputChannel(t0, chout);
outconfig.MapOutputChannel(channelIndex, chout);
FileStream fs = new FileStream("sensor" + component.Id + "_type" + component.Properties.Type + "_mode" + component.Configuration.SampleMode + "_channel" + k + ".csv", FileMode.OpenOrCreate);
StreamWriter sw = new StreamWriter(fs);
ChannelDataStreams.Add(chout.Id, sw);
channelIndex++;
}
}
}
return outconfig;
}
Data Collection
Before starting data collection, set the collection time. Finally, somewhere in your code you can issue a start command that will begin data collection. The data will then be passed as a ComponentDataReadyEventArgs member in your CollectionDataReady callback (in this case PCollectionDataReady.)
//start the data collection.
PipelineController.Instance.PipelineIds[0].Start();
Data will appear in the DataReady event created in the Configure Components and Data Source section.
Access collected data:
// This is the main entry point for data collection.
private void PCollectionDataReady(object sender, ComponentDataReadyEventArgs e)
{
foreach(var dataChannel in e.Data)
{
foreach (var point in dataChannel.Data)
{
// This loop goes through each point collected by the first sensor
}
}
}
What's next?
You're hopefully walking away from this tutorial with a solid understanding of how the Delsys API's pipeline works. Next up, you should compare the code you wrote to the Basic Streaming example (if that didn't serve as your foundation for this tutorial) and check out the Transform example!