Skip to content
This repository was archived by the owner on Apr 19, 2023. It is now read-only.

Commit

Permalink
Add cumulative mAh value using currentADC, add simulated current meter
Browse files Browse the repository at this point in the history
  • Loading branch information
thenickdude committed Mar 30, 2015
1 parent e157ca5 commit d112737
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ BIN_DIR = $(ROOT)/obj

# Source files common to all targets
COMMON_SRC = parser.c tools.c platform.c stream.c decoders.c units.c
DECODER_SRC = $(COMMON_SRC) blackbox_decode.c gpxwriter.c imu.c
DECODER_SRC = $(COMMON_SRC) blackbox_decode.c gpxwriter.c imu.c battery.c
RENDERER_SRC = $(COMMON_SRC) blackbox_render.c datapoints.c embeddedfont.c expo.c imu.c
ENCODER_TESTBED_SRC = $(COMMON_SRC) encoder_testbed.c

Expand Down
11 changes: 9 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@ Options:
--unit-amperage <unit> Current meter unit (raw|mA|A), default is A (amps)
--unit-frame-time <unit> Frame timestamp unit (us|s), default is us (microseconds)
--unit-height <unit> Height unit (m|cm|ft), default is cm (centimeters)
--unit-rotation <unit> Rate of rotation unit (raw|deg/s|rad/s), default is deg/s (degrees per second)
--unit-acceleration <u> Acceleration unit (raw|g|m/s2), default is g (9.8m/s/s)
--unit-rotation <unit> Rate of rotation unit (raw|deg/s|rad/s), default is raw
--unit-acceleration <u> Acceleration unit (raw|g|m/s2), default is raw
--unit-gps-speed <unit> GPS speed unit (mps|kph|mph), default is mps (meters per second)
--unit-vbat <unit> Vbat unit (raw|mV|V), default is V (volts)
--merge-gps Merge GPS data into the main CSV log file instead of writing it separately
--simulate-current-meter Simulate a virtual current meter using throttle data
--sim-current-meter-scale Override the FC's settings for the current meter simulation
--sim-current-meter-offset Override the FC's settings for the current meter simulation
--simulate-imu Compute tilt/roll/heading fields from gyro/accel/mag data
--imu-ignore-mag Ignore magnetometer data when computing heading
--declination <val> Set magnetic declination in degrees.minutes format (e.g. -12.58 for New York)
Expand Down Expand Up @@ -130,6 +133,10 @@ output folder for the "render to:" setting. Then click the "add job to render qu
"start render" button on the right to begin rendering the output.

## Building tools
If you just want to download some prebuilt versions of these tools, head to the "releases" tab on the GitHub
page. However, if you want to build your own binaries, or you're on Linux where we haven't provided binaries, please
read on.

The `blackbox_decode` tool for turning binary flight logs into CSV doesn't depend on any libraries, so can be built by
running `make obj/blackbox_decode`. You can add the resulting `obj/blackbox_decode` program to your system path to
make it easier to run.
Expand Down
57 changes: 57 additions & 0 deletions src/battery.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <stdint.h>

#include "battery.h"

#define SECONDS_PER_HOUR 3600u
#define MICROSECONDS_PER_SECOND 1000000u
// Careful, it nearly overflows a uint32_t:
#define MICROSECONDS_PER_HOUR (SECONDS_PER_HOUR * MICROSECONDS_PER_SECOND)

#define MILLIAMPS_PER_CENTIAMP 10

/**
* Reset/initialise the state of the passed current meter.
*/
void currentMeterInit(currentMeterState_t *state)
{
state->lastTime = 0;

state->energyMilliampHours = 0;
state->currentMilliamps = 0;
}

/**
* Update the state of the current meter by estimating the current using the RC throttle position and current time you provide.
*
* Time is an absolute time in micro-seconds (not a delta).
*/
void currentMeterUpdateVirtual(currentMeterState_t *state, int16_t currentMeterOffset, int16_t currentMeterScale, uint32_t throttle, uint32_t time)
{
int32_t throttleOffset, throttleFactor;

// Current consumption due to idling while armed (zero-throttle current usage):
state->currentMilliamps = (int32_t)currentMeterOffset * MILLIAMPS_PER_CENTIAMP;

// Current consumption based on throttle position:
throttleOffset = (int32_t)throttle - 1000;
throttleFactor = throttleOffset + (throttleOffset * throttleOffset / 50);

state->currentMilliamps += throttleFactor * (int32_t)currentMeterScale / 100;

if (state->lastTime != 0) {
state->energyMilliampHours += ((double) state->currentMilliamps * (time - state->lastTime)) / MICROSECONDS_PER_HOUR;
}

state->lastTime = time;
}

void currentMeterUpdateMeasured(currentMeterState_t *state, int16_t amperageMilliamps, uint32_t time)
{
state->currentMilliamps = amperageMilliamps;

if (state->lastTime != 0) {
state->energyMilliampHours += ((double) state->currentMilliamps * (time - state->lastTime)) / MICROSECONDS_PER_HOUR;
}

state->lastTime = time;
}
15 changes: 15 additions & 0 deletions src/battery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef BATTERY_H_
#define BATTERY_H_

typedef struct currentMeterState_t {
uint32_t lastTime;

double energyMilliampHours;
int32_t currentMilliamps;
} currentMeterState_t;

void currentMeterInit(currentMeterState_t *state);
void currentMeterUpdateVirtual(currentMeterState_t *state, int16_t currentMeterOffset, int16_t currentMeterScale, uint32_t throttle, uint32_t time);
void currentMeterUpdateMeasured(currentMeterState_t *state, int16_t amperageMilliamps, uint32_t time);

#endif
133 changes: 108 additions & 25 deletions src/blackbox_decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,35 @@
#include "tools.h"
#include "gpxwriter.h"
#include "imu.h"
#include "battery.h"
#include "units.h"

typedef struct decodeOptions_t {
int help, raw, limits, debug, toStdout;
int logNumber;
int simulateIMU, imuIgnoreMag;
int simulateCurrentMeter;
int mergeGPS;
const char *outputPrefix;

bool overrideSimCurrentMeterOffset, overrideSimCurrentMeterScale;
int16_t simCurrentMeterOffset, simCurrentMeterScale;

Unit unitGPSSpeed, unitFrameTime, unitVbat, unitAmperage, unitHeight, unitAcceleration, unitRotation;
} decodeOptions_t;

decodeOptions_t options = {
.help = 0, .raw = 0, .limits = 0, .debug = 0, .toStdout = 0,
.logNumber = -1,
.simulateIMU = false, .imuIgnoreMag = 0,
.simulateCurrentMeter = false,
.mergeGPS = 0,

.overrideSimCurrentMeterOffset = false,
.overrideSimCurrentMeterScale = false,

.simCurrentMeterOffset = 0, .simCurrentMeterScale = 0,

.outputPrefix = NULL,

.unitGPSSpeed = UNIT_METERS_PER_SECOND,
Expand Down Expand Up @@ -75,6 +86,9 @@ static FILE *csvFile = 0, *eventFile = 0, *gpsCsvFile = 0;
static char *eventFilename = 0, *gpsCsvFilename = 0;
static gpxWriter_t *gpx = 0;

// Computed states:
static currentMeterState_t currentMeterMeasured;
static currentMeterState_t currentMeterVirtual;
static attitude_t attitude;

static Unit mainFieldUnit[FLIGHT_LOG_MAX_FIELDS];
Expand Down Expand Up @@ -180,28 +194,53 @@ void createGPSCSVFile(flightLog_t *log)
}
}

static void updateIMU(flightLog_t *log, int32_t *frame, uint32_t currentTime, attitude_t *result)
static void updateSimulations(flightLog_t *log, int32_t *frame, uint32_t currentTime)
{
int16_t gyroData[3];
int16_t accSmooth[3];
int16_t magADC[3];

bool hasMag = log->mainFieldIndexes.magADC[0] > -1;
bool hasThrottle = log->mainFieldIndexes.rcCommand[3] != -1;
bool hasAmperageADC = log->mainFieldIndexes.amperageLatest != -1;

int i;

for (i = 0; i < 3; i++) {
gyroData[i] = (int16_t) frame[log->mainFieldIndexes.gyroData[i]];
accSmooth[i] = (int16_t) frame[log->mainFieldIndexes.accSmooth[i]];
}

if (hasMag && !options.imuIgnoreMag) {
if (options.simulateIMU) {
for (i = 0; i < 3; i++) {
magADC[i] = (int16_t) frame[log->mainFieldIndexes.magADC[i]];
gyroData[i] = (int16_t) frame[log->mainFieldIndexes.gyroData[i]];
accSmooth[i] = (int16_t) frame[log->mainFieldIndexes.accSmooth[i]];
}

if (hasMag && !options.imuIgnoreMag) {
for (i = 0; i < 3; i++) {
magADC[i] = (int16_t) frame[log->mainFieldIndexes.magADC[i]];
}
}

updateEstimatedAttitude(gyroData, accSmooth, hasMag && !options.imuIgnoreMag ? magADC : NULL,
currentTime, log->sysConfig.acc_1G, log->sysConfig.gyroScale, &attitude);
}

if (hasAmperageADC) {
currentMeterUpdateMeasured(
&currentMeterMeasured,
flightLogAmperageADCToMilliamps(log, frame[log->mainFieldIndexes.amperageLatest]),
currentTime
);
}

updateEstimatedAttitude(gyroData, accSmooth, hasMag && !options.imuIgnoreMag ? magADC : NULL,
currentTime, log->sysConfig.acc_1G, log->sysConfig.gyroScale, result);
if (options.simulateCurrentMeter && hasThrottle) {
int16_t throttle = frame[log->mainFieldIndexes.rcCommand[3]];

currentMeterUpdateVirtual(
&currentMeterVirtual,
options.overrideSimCurrentMeterOffset ? options.simCurrentMeterOffset : log->sysConfig.currentMeterOffset,
options.overrideSimCurrentMeterScale ? options.simCurrentMeterScale : log->sysConfig.currentMeterScale,
throttle,
currentTime
);
}
}

/**
Expand Down Expand Up @@ -281,7 +320,23 @@ void outputGPSFrame(flightLog_t *log, int32_t *frame)
}
}

static bool printfMainFieldInUnit(flightLog_t *log, FILE *file, int fieldIndex, int32_t fieldValue, Unit unit)
static void fprintfMilliampsInUnit(FILE *file, int32_t milliamps, Unit unit)
{
switch (unit) {
case UNIT_AMPS:
fprintf(file, "%.3f", milliamps / 1000.0);
break;
case UNIT_MILLIAMPS:
fprintf(file, "%d", milliamps);
break;
default:
fprintf(stderr, "Bad amperage unit %d\n", (int) unit);
exit(-1);
break;
}
}

static bool fprintfMainFieldInUnit(flightLog_t *log, FILE *file, int fieldIndex, int32_t fieldValue, Unit unit)
{
/* Convert the fieldValue to the given unit based on the original unit of the field (that we decide on by looking
* for a well-known field that corresponds to the given fieldIndex.)
Expand All @@ -300,14 +355,9 @@ static bool printfMainFieldInUnit(flightLog_t *log, FILE *file, int fieldIndex,
}
break;
case UNIT_AMPS:
if (fieldIndex == log->mainFieldIndexes.amperageLatest) {
fprintf(file, "%.3f", flightLogAmperageADCToMilliamps(log, (uint16_t)fieldValue) / 1000.0);
return true;
}
break;
case UNIT_MILLIAMPS:
if (fieldIndex == log->mainFieldIndexes.amperageLatest) {
fprintf(file, "%u", flightLogAmperageADCToMilliamps(log, (uint16_t)fieldValue));
fprintfMilliampsInUnit(file, flightLogAmperageADCToMilliamps(log, (uint16_t)fieldValue), unit);
return true;
}
break;
Expand Down Expand Up @@ -409,13 +459,13 @@ void outputMainFrameFields(flightLog_t *log, uint32_t frameTime, int32_t *frame)
if (frameTime == (uint32_t) -1)
fprintf(csvFile, "X");
else {
if (!printfMainFieldInUnit(log, csvFile, i, frame[i], mainFieldUnit[i])) {
if (!fprintfMainFieldInUnit(log, csvFile, i, frame[i], mainFieldUnit[i])) {
fprintf(stderr, "Bad unit for field %d\n", i);
exit(-1);
}
}
} else {
if (!printfMainFieldInUnit(log, csvFile, i, frame[i], mainFieldUnit[i])) {
if (!fprintfMainFieldInUnit(log, csvFile, i, frame[i], mainFieldUnit[i])) {
fprintf(stderr, "Bad unit for field %d\n", i);
exit(-1);
}
Expand All @@ -425,6 +475,19 @@ void outputMainFrameFields(flightLog_t *log, uint32_t frameTime, int32_t *frame)
if (options.simulateIMU) {
fprintf(csvFile, ", %.2f, %.2f, %.2f", attitude.roll * 180 / M_PI, attitude.pitch * 180 / M_PI, attitude.heading * 180 / M_PI);
}

if (log->mainFieldIndexes.amperageLatest != -1) {
// Integrate the ADC's current measurements to get cumulative energy usage
fprintf(csvFile, ", %d", (int) round(currentMeterMeasured.energyMilliampHours));
}

if (options.simulateCurrentMeter) {
fprintf(csvFile, ", ");

fprintfMilliampsInUnit(csvFile, currentMeterVirtual.currentMilliamps, options.unitAmperage);

fprintf(csvFile, ", %d", (int) round(currentMeterVirtual.energyMilliampHours));
}
}

void outputMergeFrame(flightLog_t *log)
Expand Down Expand Up @@ -492,9 +555,7 @@ void onFrameReadyMerge(flightLog_t *log, bool frameValid, int32_t *frame, uint8_
lastFrameTime = (uint32_t) frame[FLIGHT_LOG_FIELD_INDEX_TIME];
}

if (options.simulateIMU) {
updateIMU(log, frame, lastFrameTime, &attitude);
}
updateSimulations(log, frame, lastFrameTime);

/*
* Store this frame to print out later since we don't know if a GPS frame follows it yet.
Expand Down Expand Up @@ -528,9 +589,7 @@ void onFrameReady(flightLog_t *log, bool frameValid, int32_t *frame, uint8_t fra
if (frameValid || (frame && options.raw)) {
lastFrameTime = (uint32_t) frame[FLIGHT_LOG_FIELD_INDEX_TIME];

if (options.simulateIMU) {
updateIMU(log, frame, lastFrameTime, &attitude);
}
updateSimulations(log, frame, lastFrameTime);

outputMainFrameFields(log, frameValid ? (uint32_t) frame[FLIGHT_LOG_FIELD_INDEX_TIME] : (uint32_t) -1, frame);

Expand Down Expand Up @@ -647,6 +706,14 @@ void writeMainCSVHeader(flightLog_t *log)
fprintf(csvFile, ", roll, pitch, heading");
}

if (log->mainFieldIndexes.amperageLatest != -1) {
fprintf(csvFile, ", energyCumulative (mAh)");
}

if (options.simulateCurrentMeter) {
fprintf(csvFile, ", currentVirtual (%s), energyCumulativeVirtual (mAh)", UNIT_NAME[options.unitAmperage]);
}

if (options.mergeGPS && log->gpsFieldCount > 0) {
fprintf(csvFile, ", ");

Expand Down Expand Up @@ -944,6 +1011,9 @@ void printUsage(const char *argv0)
" --unit-gps-speed <unit> GPS speed unit (mps|kph|mph), default is mps (meters per second)\n"
" --unit-vbat <unit> Vbat unit (raw|mV|V), default is V (volts)\n"
" --merge-gps Merge GPS data into the main CSV log file instead of writing it separately\n"
" --simulate-current-meter Simulate a virtual current meter using throttle data\n"
" --sim-current-meter-scale Override the FC's settings for the current meter simulation\n"
" --sim-current-meter-offset Override the FC's settings for the current meter simulation\n"
" --simulate-imu Compute tilt/roll/heading fields from gyro/accel/mag data\n"
" --imu-ignore-mag Ignore magnetometer data when computing heading\n"
" --declination <val> Set magnetic declination in degrees.minutes format (e.g. -12.58 for New York)\n"
Expand Down Expand Up @@ -971,6 +1041,8 @@ void parseCommandlineOptions(int argc, char **argv)
enum {
SETTING_PREFIX = 1,
SETTING_INDEX,
SETTING_CURRENT_METER_OFFSET,
SETTING_CURRENT_METER_SCALE,
SETTING_DECLINATION,
SETTING_DECLINATION_DECIMAL,
SETTING_UNIT_GPS_SPEED,
Expand All @@ -992,7 +1064,10 @@ void parseCommandlineOptions(int argc, char **argv)
{"stdout", no_argument, &options.toStdout, 1},
{"merge-gps", no_argument, &options.mergeGPS, 1},
{"simulate-imu", no_argument, &options.simulateIMU, 1},
{"simulate-current-meter", no_argument, &options.simulateCurrentMeter, 1},
{"imu-ignore-mag", no_argument, &options.imuIgnoreMag, 1},
{"sim-current-meter-scale", required_argument, 0, SETTING_CURRENT_METER_SCALE},
{"sim-current-meter-offset", required_argument, 0, SETTING_CURRENT_METER_OFFSET},
{"declination", required_argument, 0, SETTING_DECLINATION},
{"declination-dec", required_argument, 0, SETTING_DECLINATION_DECIMAL},
{"prefix", required_argument, 0, SETTING_PREFIX},
Expand Down Expand Up @@ -1071,6 +1146,14 @@ void parseCommandlineOptions(int argc, char **argv)
case SETTING_DECLINATION_DECIMAL:
imuSetMagneticDeclination(atof(optarg));
break;
case SETTING_CURRENT_METER_SCALE:
options.overrideSimCurrentMeterScale = true;
options.simCurrentMeterScale = atoi(optarg);
break;
case SETTING_CURRENT_METER_OFFSET:
options.overrideSimCurrentMeterOffset = true;
options.simCurrentMeterOffset = atoi(optarg);
break;
case '\0':
//Longopt which has set a flag
break;
Expand Down

0 comments on commit d112737

Please sign in to comment.