Labrador/Librador_API/___librador/librador_shared_library/o1buffer.cpp

259 lines
8.8 KiB
C++

#include "o1buffer.h"
#include "logging_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//o1buffer is an object that has o(1) access times for its elements.
//At the moment it's basically an array, but I'm keeping it as an object so it can be changed to something more memory efficient later.
//See isobuffer in github.com/espotek/labrador for an example of a much more compact (RAM-wise) buffer.
o1buffer::o1buffer()
{
buffer = (int *) (malloc(sizeof(int)*NUM_SAMPLES_PER_CHANNEL));
}
o1buffer::~o1buffer(){
free(buffer);
}
int o1buffer::reset(bool hard){
mostRecentAddress = 0;
stream_index_at_last_call = 0;
if(hard){
for (int i=0; i<NUM_SAMPLES_PER_CHANNEL; i++){
buffer[i] = 0;
}
}
return 0;
}
void o1buffer::add(int value, int address){
//Ensure that the address is not too high.
if(address >= NUM_SAMPLES_PER_CHANNEL){
address = address % NUM_SAMPLES_PER_CHANNEL;
}
if(address<0){
LIBRADOR_LOG(LOG_ERROR, "ERROR: o1buffer::add was given a negative address\n");
}
//Assign the value
buffer[address] = value;
updateMostRecentAddress(address);
}
int o1buffer::addVector(int *firstElement, int numElements){
int currentAddress = mostRecentAddress;
for(int i=0; i< numElements; i++){
add(firstElement[i], currentAddress);
currentAddress = (currentAddress + 1) % NUM_SAMPLES_PER_CHANNEL;
}
return 0;
}
int o1buffer::addVector(char *firstElement, int numElements){
int currentAddress = mostRecentAddress;
for(int i=0; i< numElements; i++){
add(firstElement[i], currentAddress);
currentAddress = (currentAddress + 1) % NUM_SAMPLES_PER_CHANNEL;
}
return 0;
}
int o1buffer::addVector(unsigned char *firstElement, int numElements){
int currentAddress = mostRecentAddress;
for(int i=0; i< numElements; i++){
add(firstElement[i], currentAddress);
currentAddress = (currentAddress + 1) % NUM_SAMPLES_PER_CHANNEL;
}
return 0;
}
int o1buffer::addVector(short *firstElement, int numElements){
int currentAddress = mostRecentAddress;
for(int i=0; i< numElements; i++){
add(firstElement[i], currentAddress);
currentAddress = (currentAddress + 1) % NUM_SAMPLES_PER_CHANNEL;
}
return 0;
}
int o1buffer::get(int address){
//Ensure that the address is not too high.
if(address >= NUM_SAMPLES_PER_CHANNEL){
address = address % NUM_SAMPLES_PER_CHANNEL;
}
if(address<0){
LIBRADOR_LOG(LOG_ERROR, "ERROR: o1buffer::get was given a negative address\n");
}
//Return the value
return buffer[address];
}
inline void o1buffer::updateMostRecentAddress(int newAddress){
mostRecentAddress = newAddress;
}
//This function places samples in a buffer than can be plotted on the streamingDisplay.
//A small delay, is added in case the packets arrive out of order.
std::vector<double> *o1buffer::getMany_double(int numToGet, int interval_samples, int delay_samples, int filter_mode, double scope_gain, bool AC, bool twelve_bit_multimeter){
//Resize the vector
convertedStream_double.resize(numToGet);
//Copy raw samples out.
int tempAddress;
for(int i=0;i<numToGet;i++){
tempAddress = mostRecentAddress - delay_samples - (interval_samples * i);
if(tempAddress < 0){
tempAddress += NUM_SAMPLES_PER_CHANNEL;
}
double *data = convertedStream_double.data();
data[i] = get_filtered_sample(tempAddress, filter_mode, interval_samples, scope_gain, AC, twelve_bit_multimeter);
//convertedStream_double.replace(i, buffer[tempAddress]);
}
return &convertedStream_double;
}
//Reads each int as 8 bools. Upper 3 bytes are ignored.
std::vector<uint8_t> *o1buffer::getMany_singleBit(int numToGet, int interval_subsamples, int delay_subsamples){
//Resize the vector
convertedStream_digital.resize(numToGet);
//Copy raw samples out.
int tempAddress;
int subsample_current_delay;
uint8_t mask;
uint8_t *data = convertedStream_digital.data();
int tempInt;
for(int i=0;i<numToGet;i++){
subsample_current_delay = delay_subsamples + (interval_subsamples * i);
tempAddress = mostRecentAddress - subsample_current_delay / 8;
mask = 0x01 << (subsample_current_delay % 8);
if(tempAddress < 0){
tempAddress += NUM_SAMPLES_PER_CHANNEL;
}
tempInt = get(tempAddress);
data[i] = (((uint8_t)tempInt) & mask) ? 1 : 0;
}
return &convertedStream_digital;
}
std::vector<double> *o1buffer::getSinceLast(int feasible_window_begin, int feasible_window_end, int interval_samples, int filter_mode, double scope_gain, bool AC, bool twelve_bit_multimeter){
//Calculate what sample the feasible window begins at
//printf_debugging("o1buffer::getSinceLast()\n")
int feasible_start_point = mostRecentAddress - feasible_window_begin;
if(feasible_start_point < 0){
feasible_start_point += NUM_SAMPLES_PER_CHANNEL;
}
//Work out whether or not we're starting from the feasible window or the last point
int actual_start_point;
if(distanceFromMostRecentAddress(feasible_start_point) > distanceFromMostRecentAddress(stream_index_at_last_call + interval_samples)){
actual_start_point = stream_index_at_last_call + interval_samples;
} else {
actual_start_point = feasible_start_point;
}
//Work out how much we're copying
int actual_sample_distance = distanceFromMostRecentAddress(actual_start_point) - distanceFromMostRecentAddress(mostRecentAddress - feasible_window_end);
int numToGet = actual_sample_distance/interval_samples;
//printf_debugging("Fetching %d samples, starting at index %d with interval %d\n", numToGet, actual_start_point, interval_samples);
//Set up the buffer
convertedStream_double.resize(numToGet);
//Copy raw samples out.
int tempAddress = stream_index_at_last_call;
for(int i=0;i<numToGet;i++){
tempAddress = actual_start_point + (interval_samples * i);
if(tempAddress >= NUM_SAMPLES_PER_CHANNEL){
tempAddress -= NUM_SAMPLES_PER_CHANNEL;
}
double *data = convertedStream_double.data();
data[numToGet-1-i] = get_filtered_sample(tempAddress, filter_mode, interval_samples, scope_gain, AC, twelve_bit_multimeter);
//convertedStream_double.replace(i, buffer[tempAddress]);
}
//update stream_index_at_last_call for next call
stream_index_at_last_call = tempAddress;
return &convertedStream_double;
}
int o1buffer::distanceFromMostRecentAddress(int index){
//Standard case. buffer[NUM_SAMPLES_PER_CHANNEL] not crossed between most recent and index's sample writes.
if(index < mostRecentAddress){
return mostRecentAddress - index;
}
//Corner case. buffer[NUM_SAMPLES_PER_CHANNEL] boundary has been crossed.
if(index > mostRecentAddress){
//Two areas. 0 to mostRecentAddress, and index to the end of the buffer.
return mostRecentAddress + (NUM_SAMPLES_PER_CHANNEL - index);
}
//I guess the other corner case is when the addresses are the same.
return 0;
}
//replace with get_filtered_sample
double o1buffer::get_filtered_sample(int index, int filter_type, int filter_size, double scope_gain, bool AC, bool twelve_bit_multimeter){
double accum = 0;
int currentPos = index - (filter_size / 2);
int end = currentPos + filter_size;
switch(filter_type){
case 0: //No filter
return sampleConvert(buffer[index], scope_gain, AC, twelve_bit_multimeter);
case 1: //Moving Average filter
if(currentPos < 0){
currentPos += NUM_SAMPLES_PER_CHANNEL;
}
if(end >= NUM_SAMPLES_PER_CHANNEL){
end -= NUM_SAMPLES_PER_CHANNEL;
}
while(currentPos != end){
accum += buffer[currentPos];
currentPos = (currentPos + 1) % NUM_SAMPLES_PER_CHANNEL;
}
return sampleConvert(accum/((double)filter_size), scope_gain, AC, twelve_bit_multimeter);
break;
default: //Default to "no filter"
return buffer[index];
}
}
double o1buffer::sampleConvert(int sample, double scope_gain, bool AC, bool twelve_bit_multimeter){
double voltageLevel;
double TOP;
if(twelve_bit_multimeter){
TOP = 2048;
} else TOP = 128;
voltageLevel = ((double)sample * (vcc/2)) / (frontendGain * scope_gain * TOP);
if (!twelve_bit_multimeter) voltageLevel += voltage_ref;
#ifdef MULTIMETER_INVERT
if(twelve_bit_multimeter) voltageLevel *= -1;
#endif
if(AC){
voltageLevel -= voltage_ref;
}
if(twelve_bit_multimeter){
#warning Hack here. Do not know why this line works, but it does.
voltageLevel = voltageLevel / 16;
}
return voltageLevel;
}