Logging to a vbo

One of the key features of the VBOX Touch is its ability to log GNSS, CAN and Serial data. This allows the data to be viewed with our VBOX Test Suite software which provides lots of useful analysis options.

In this section, we will talk about how you can create a log onto a vbo file.

Basic logging

A basic log is a log of the GNSS data. This is also provided as samples as can be seen in the previous section.

As you may be able to guess by the name of the file type a .vbo file uses the vbo module. This is imported as follows.

import vbo

Starting a log

Before you can start a log file you will need to have an SD Card inserted into your VBOX Touch. There are commands to check this as can be seen in the SD Card tutorial.

Once you have an SD card inserted you will need to start the log itself using the start function().

vbo.start()

This will handle all the logging for you and will log every sample as it comes. It will create a new file with the first number not taken i.e VBOX0007.vbo if VBOX0001 to VBOX0006 already existed on your SD Card. It also returns this file name to you if you want to store and use this.

If you haven’t got an SD Card inserted then it will throw a RuntimeError: No SD Card. Or if you are already logging then it will throw a RuntimeError: Logging Busy. Which can help you pinpoint what you are doing wrong.

Pause a log

Sometimes you may need to pause the log. This can be when you are getting irrelevant data that isn’t useful to you and you don’t want to include it in the vbo file.

vbo.pause(start)

It takes 1 parameter of a boolean whether to start/stop logging.

An example of this being used practically is if the speed is under 1mph and you don’t want this messing with averages of your other data of when you are moving. This is implemented in the code below:

import vbox
import vbo

def moving_logging(speed): # controls the pausing of the vbo logging when not moving
    if speed > 0.45: # if speed is greater than 0.45m/s (approx 1mph)
        if vbo.get_status() != 0: # very basic log check, if copying then look into a better method below.
            if not(vbo.get_status() & vbo.VBO_STATUS_PAUSED):
                print("PAUSING")
                vbo.pause(True)
    else:
        if vbo.get_status() != 0:
            if vbo.get_status() & vbo.VBO_STATUS_PAUSED:
                print("RESUMING FROM PAUSE")
                vbo.pause(False)


sample = vbox.get_sample_hp()
moving_logging(sample.speed_gnd_smooth_mps)

Stopping a log

Stopping a log will stop logging to the file that the vbo.start() function returned.

vbo.stop()

It doesn’t have a return value (returns None). If this function is called when there isn’t a log currently taking place it does not throw an error. This means if you want to see if it is in a stopped state you need to check vbo.get_status() is equal to 0 as is talked about below.

Checking the current logging status

To check the current logging status you need to use the get_status function which will return the state(s) that the logging is in.

vbo.get_status()

This will return the current status:

  • No logging = 0

  • Opening file = 1

  • Ready for data = 2

  • Writing to file = 4

  • Paused = 8

  • Closing = 32

  • Resume logging = 64

When it gets the status it will add all the ones it is currently in e.g. When you call get status when in a pause you get 10, which means it is ready for data (2) plus paused (8). You can check this easily using python &. This will allow you to and the value with another and if this returns 1 then it is in 10. For example:

if vbo.get_status() & vbo.VBO_STATUS_READY:
    print("READY TO WRITE")

Advanced logging

As mentioned at the beginning of this section you can log any data, this includes CAN and Serial data. However, you will need to pass it the data and tell it to log this data.

Creating logging channels

To simplify the process of creating a .vbo file with the information that you want recording there is a class called upy_channels (new as of 1.5). This can be used with vbo.start() to create a .vbo log file that contains new data external to the GNSS engine.

Before you can create your logging channel you first need to stop any logging that is currently taking place. If you try and create a upy channel before this, then it will throw a MemoryError: uPy Channel objects cannot be created during logging.

To create a uPy channel object you need to initialise an instance of the object using the following:

a_channel = vbo.upy_channel("MyChannel", "", "MyChannel")

The object takes 3 input parameters:

  • channel_Name[string] = The name of the channel to be written to the vbo file

  • units[string] = The units of the channel for example ‘m/s’ for speed

  • reference_Name[string] = The name you will use in the code to reference the channel. This will also be the column name of your channel in the vbo file. WARNING: THIS CANNOT HAVE WHITE SPACE. Can be the same as the channel_Name.

Make sure you do not name these after an existing channel name as this could get confusing. For example, if you are taking lateral acceleration from a CAN device you could call it “CANLatAcc”.

Once you have instantiated this object there is no way to change any of the object data that you gave it other than to remove it.

Removing your channels

Similarly, when you are creating a channel, to remove a channel you first need to stop logging. If you try and remove a upy channel before this then it will throw a MemoryError: Attempted to remove uPy Channel object during logging.

To then remove a upy channel use the remove() function from the vbo module.

a_channel.remove()

To check this is all working properly you can try the snippet of code below.

import vbo
import vts
a_channel = vbo.upy_channel("MyChannel", "", "MyChannel")
file1 = vbo.start()
vts.delay_ms(1000) # Using a delay to ensure that there has been time to create a file.
vbo.stop()
vts.delay_ms(1000) # Using a delay to ensure that there is time for logging to be fully stopped before logging a new file.
a_channel.remove()
file2 = vbo.start()
vts.delay_ms(1000)
vbo.stop()
print("LOGGED FILES: {}, {}".format(file1, file2))

Open the 2 files it says it logged in a text editor and see what data headers are there. You should see that only the first logged file has the upy_channel “MyChannel” and that the second file has not got this header.

Setting Channel Data

Once a channel has been created you need to give it data otherwise there is no point in having it. To set some data on one of your channels you need to use the set_value() function.

sample_id = vbox.get_sample_hp().sample_id
a_channel.set_value(sample_id, 1.23)

As you can see above this function has 2 input parameters.

  • sample_id[int] = The sample_id of the current sample that you are logging. This ranges from 0-127 inclusive. We almost always use the current sample_id as this allows the data being added to sync with the GNSS engine data and hence provides more meaningful results.

  • data[int/float] = Any floating point number.

The sample_id is very important as this is what will be used to keep it in sync with the GNSS data. The data value has to be a float or an int (as this can be converted to a float) as all vbo data is stored as floats.

Getting Channel Data

If you want to get the value that you have set for the upy channel you can reference an sample_id that you have set and it will return the value of this sample_id.

For example:

print(a_channel[10]) # prints 0.0
a_channel.set_value(10, 1.23) # sets sample_id 10 to equal 1.23
print(a_channel[10]) # prints 1.23

This will print 0.0 and then 1.23. If you enter an sample_id that you have not yet set a value for you will see that it is filled with 0.0. This is because it operates on a circular buffer from 0-127 and to begin with, all these values are set to equal 0.0.

If you were to try doing a slice of sample_id or a sample_id out of range then a TypeError and IndexError will be thrown.

Clearing the channel data

To reset/clear the buffer of all data that it is currently storing you can use the clear_buffer function.

a_channel.clear_buffer()

This will set all the values in the buffer to 0. This would usually only be used when a log has finished or when you have finished the buffer and don’t want to repeat data if you don’t set the channel value for that index again. A better alternative for not repeating the data is setting the value to 0 if you aren’t setting it to anything else as this will avoid you having to time the clearing of the buffer correctly.

Writing to the .vbo

Once you have set your data then you need to write it along with the GNSS data to the .vbo file. To flag this write to the C code you use the write_upy_channels function.

vbo.write_upy_channels(sample_id)

This takes an input of the sample_id in the buffer that you wish to write.

  • sample_id[int] = This is the sample_id of what to write from the buffer, it is associated with the sample_id.

You can call this function even if you haven’t set new data. When you do so it will write the same data as it did for the previous sample. This may be 0.0 if you haven’t set a value or have cleared the buffer. This function will set a flag to say that the sample is completed and that the line can be written. The sample will then be written whenever the processor is free to do so which is normally very soon after it has been flagged.

There are a few things to note with this function, the first being that you must call it if you want to write data to a .vbo file when you have a upy channel. It stops the GNSS data from automatically being logged. However, this isn’t too hard to fix, if you make sure to write at the end of the GNSS callback.

import vbox
import vbo

def new_sample_cb():
    processed = 1
    while processed:
        sample = vbox.get_sample_hp(False)
        if sample is not None:
            process_sample(sample)
        else:
            processed = 0

def process_sample(sample):
    # write all other processing that you want to do with the data here.

    if vbo.get_status() & vbo.VBO_STATUS_READY: # checks if logging is taking place which will stop the exception
        vbo.write_upy_channels(sample.sample_id)

vbox.set_new_data_callback(new_sample_cb)

As mentioned in the comment in the code above if you try and call this function while logging is not active then it will throw a “RuntimeError: Logging is not active”. Which will also help you instantly see your mistake.

Note:

When a sample_id is provided that is ahead of the previously logged sample_id, any samples that were not yet logged will be written to ensure synchronization with the data.

Pending samples

Sometimes you may fall behind a few samples and it is important to know this when debugging your program. Hence there is the pending_samples function.

vbo.pending_samples()

This will return how many samples you are behind. You could print this information straight back to yourself. However, a better option would be to create a upy_channel with this information. This is because it allows you to see if there are specific points in time/other spikes in data that are causing the samples to queue.