LORD-MicroStrain / MSCL

MicroStrain Communication Library
https://www.microstrain.com/software/mscl
MIT License
78 stars 57 forks source link

New Sensors and USB Beacon #206

Open BaldeepSinghAtMack opened 2 years ago

BaldeepSinghAtMack commented 2 years ago

Hello i have a couple of questions. I was pondering if i should make multiple issues but i think staying with one thread would be lucid.


Till now, we have been solving our Measurement requirements with following LORD System: I. Lord Microstrain WSDA-Base-LXRS Base 104 as Beacon. II. Lord Microstrain G-Link Wireless Acc. Node and V-Link LXRS as Sensors. III. Our own MSCL based Application (using mscl version 41.0.8.0)

Measurement System looked like: 3 G-Link's and 1 V-Link used with Data Arm Method / Log -nly Mode. I gave each Data Log a 'name string' during arming which we used later as a reference to our measurement. This naming/labelling was very important for us for retracing our measurement. Now, our Procurement-Colleagues have supplied me with WSDA-200-USB as beacon and G-Link-200-40G as Acc. sensor.

They are new models and looks also quite different. I have determined that we cannot use Data-Arm Mode anymore as it used to be. I downloaded the new mscl 62.1.2.0. It did'nt helped.

My Questions are:

Thanks in advance, Regards Singh

BaldeepSinghAtMack commented 2 years ago

Dear Sir/Madam, Do you require any extra information regarding my Query? Regards

msclissa commented 2 years ago

So sorry for the delay!

As you've discovered, with the new hardware the Armed Datalog sampling mode is no longer supported. I need to talk to one of our wireless experts to become more familiar with the armed datalog functionality so that I can better advise how to achieve the same or similar results and whether there will be anything to look out for that can't be replicated completely.

Regarding the drivers: the WSDA-200-USB drivers for both Windows and Linux can be found here: WSDA Drivers

I will get back to you tomorrow with more information about replicating armed datalogging. Thanks for your patience!

msclissa commented 2 years ago

Network Configuration

The closest behavior is going to be adding nodes to a SyncSamplingNetwork with a couple specific configuration options set:

// set the duration in sweep count (must be a multiple of 100) // sweeps in time duration = sample rate (sweeps/s) duration (s) uint32_t sweeps = 512 120;

// adjust to be an even 100 sweeps = sweeps / 100; sweeps += 1; // remove this if you want to adjust down instead of up sweeps *= 100;

// indicate sampling duration should be limited, set limit config.unlimitedDuration(false); config.numSweeps(sweeps);

node.applyConfig(config);

Once all nodes are added to the `SyncSamplingNetwork` and you send the start sampling command to all the nodes they will transition to sampling mode, but will not actually start storing any data until the base station outputs the first beacon. You can do both of these operations at once with the `startSampling()` function or you can ready the nodes with `startSampling_noBeacon()` and enable the beacon later.

// ensure the beacon is disabled baseStation.disableBeacon();

// add nodes to network, assuming nodes is a collection of WirelessNode objects mscl::SyncSamplingNetwork network(baseStation); for (mscl::WirelessNode node : nodes) { network.addNode(node); }

// required prior to start to ensure no unapplied changes to network config network.applyConfiguration();

// uncomment to ready nodes and start sampling immediately //network.startSampling();

// ready nodes by sending start sampling command network.startSampling_noBeacon()

// add whatever delay

// enable beacon - nodes will start recording data when they receive the first beacon baseStation.enableBeacon();


Downloading data sessions with the `DatalogDownloader` class should be the same as you have now, with the exception of reading the `userString()` which will now be empty.

#### Data Session Naming/Labeling
Unfortunately this behavior of specifying a session string on the node is no longer supported - you will need to create your own implementation for this. I would recommend doing this based on timestamp. The base station gets the timestamp from the local pc time and sends it to the nodes with the beacon. You could maintain a map of timestamps to labels and, when the `sessionIndex()` increments (`startOfSession()` true) you can lookup the new label based on the sweep timestamp.

You could also store the labels mapped to session index to keep it more logically similar to your current implementation, but it would need to be mapped for each node individually unless you were very careful about downloading and clearing all nodes at the same time.

Sorry for the inconvenience here - we'd be happy to offer additional guidance around this if you let us know more information about any specific limitations or requirements you have for this functionality!

I hope this helps, sorry again for the delay! Let us know if anything needs clarification or you have additional questions!
BaldeepSinghAtMack commented 2 years ago

Dear Sir/Madam,

First of all, i would like to thank you for responding. Trigger-Identification with the Sampling time would be the next logically perceptable implementation. I was thinking of keeping a record of time stamps and corresponding UserStrings locally while doing network.startsampling(). i must admit that storing these two on the Node would be much elegent. Do i have user accesible memory on Node? If yes how do i access it programmatically. Regards from Germany, Singh

I have two another questions for that i think its better to open new threads....

msclissa commented 2 years ago

EDIT: The following is true only for 200-series nodes (ie G-Link-200, V-Link-200)

There are 20 bytes of free, accessible memory on the node (EEPROM 550-570) - obviously that's not nearly as much as 50 bytes per datalog session, but if you only need to store one message at a time it may be sufficient. Otherwise, you may be able to use it to store information to map the node's current datalog session to a test/message id. You'd likely only be able to map a few, but depending on your use case this could be effective.

The functions used to access this memory are on the WirelessNode object: readEeprom() and writeEeprom(). Both require a memory address (even numbers between 550 and 568) and read/write uint16s.

Below I've included functions to read and write string messages to this memory as well as a sort of sample implementation for mapping datalog session ID to a user defined message ID - with this implementation you'd be able to fit 10 pairs as long as the ids fit in a uint8 (max value 255). The message ID would be consistent across nodes and the node would hold the information about which datalog session(s) the message ID corresponds to. This does not fully replace the previously available functionality, but hopefully we can figure out something that works for your application!

Write string to free memory

const uint16_t FREE_MEMORY_ADDRESS_START = 550;
const uint16_t FREE_MEMORY_ADDRESS_LENGTH = 20; // 20 bytes

void writeMessageToNodeFreeMemory(mscl::WirelessNode& node, std::string msg)
{
    // max 20-char message
    if (msg.length() > FREE_MEMORY_ADDRESS_LENGTH)
    {
        throw mscl::Error_NotSupported("Provided message too long to write to node free memory");
    }

    // write eeprom function writes u16
    // convert char -> u8 then pack two u8 into u16
    uint16_t pendingWrite = 0;

    // iterate over message and write to node memory
    // overwrite full section to clear previously written messages
    for (size_t i = 0; i < FREE_MEMORY_ADDRESS_LENGTH; i++)
    {
        // get char
        uint8_t c = i < msg.length() ? static_cast<uint8_t>(msg[i]) : ' ';

        // every second char, write previous and current char
        if (i % 2 != 0)
        {
            // shift prev char over a byte, add current char
            pendingWrite = pendingWrite << 8;
            pendingWrite += c;

            // determine write location
            uint16_t writeLocation = FREE_MEMORY_ADDRESS_START + (i - 1);

            // write to node memory
            node.writeEeprom(writeLocation, pendingWrite);
        }
        else
        {
            // store current char to be written next iteration
            pendingWrite = c;
        }
    }
}

Read string from free memory

const uint16_t FREE_MEMORY_ADDRESS_START = 550;
const uint16_t FREE_MEMORY_ADDRESS_LENGTH = 20; // 20 bytes

std::string readMessageFromNodeFreeMemory(const mscl::WirelessNode& node)
{
    std::string readMsg;

    // iterate over all free memory, read out in u16 (2 bytes)
    for (size_t i = 0; i < FREE_MEMORY_ADDRESS_LENGTH; i += 2)
    {
        // read next u16
        uint16_t readChs = node.readEeprom(FREE_MEMORY_ADDRESS_START + i);

        // unpack into two chars
        uint8_t firstCh = readChs >> 8;
        uint8_t secondCh = readChs;

        // add to message string
        readMsg += static_cast<char>(firstCh);
        readMsg += static_cast<char>(secondCh);
    }

    return readMsg;
}

Sample message:log session mapper implementation

const uint16_t FREE_MEMORY_ADDRESS_START = 550;
const uint16_t FREE_MEMORY_ADDRESS_LENGTH = 20; // 20 bytes

// set user-defined message ID, get current datalog session ID
uint8_t messageId = 6;
uint8_t currentNodeSession = node.getNumDatalogSessions() + 1; // double check if + 1 needed, unsure if session ids start at 0 or 1

// clear current message
//writeMessageToNodeFreeMemory(node, "");

// get, trim current message to find offset length
std::string currentMsg = readMessageFromNodeFreeMemory(node);
mscl::Utils::strTrim(currentMsg);
size_t currentMsgLength = currentMsg.length();

// pack message ID and datalog session ID uint8s into single uint16
uint16_t messageIdLogSessionPair = messageId;
messageIdLogSessionPair = messageIdLogSessionPair << 8;
messageIdLogSessionPair += currentNodeSession;

// check memory bounds
if (currentMsgLength + 2 > FREE_MEMORY_ADDRESS_LENGTH)
{
    throw mscl::Error_NotSupported("Provided message too long to write to node free memory");
}

// write new message ID and datalog session ID pair at end of existing written
node.writeEeprom(FREE_MEMORY_ADDRESS_START + currentMsgLength, messageIdLogSessionPair);

// read new message from node
std::string fullMsg = readMessageFromNodeFreeMemory(node);

// will print whole memory section, so before use fill memory with easily recognizable placeholder values
cout << endl << "Read message: " << endl;
for (size_t i = 0; i < fullMsg.length(); i++)
{
    // cast to uint16 to display numeric value instead of char value of uint8
    cout << static_cast<uint16_t>(fullMsg[i]);

    // output formatting
    if (i % 2 && i != 0)
    {
        cout << endl;
    }
    else
    {
        cout << " : ";
    }
}
BaldeepSinghAtMack commented 2 years ago

The discontinuation of DataLog Mode has caused some discomfort and extra work BUT I would like to let you guys know that you guys are a great help. I really appreciate that! I think with your support, we will find again a suitable and fitting solution. Thank you PS : i am gonna leave this issue open for some time till i get a feedback from our field guys that they are happy with sync. sampling mode.

Singh-Bal commented 1 year ago

Hello with Start of the Sampling, know the Time Stamp and i notice it in a csv file with a respective name. While downloading from node i compare the value of this time Stamp and i know the name accordingly.