Turns out that converting the 32bit and 40bit fixed-point binary data from normal and average modes was as simple as the 22bit fixed-point data from HS mode. The exponent bytes are used to check for error's reported by the meter, but are otherwise ignored since fluke uses fixed exponents for all measurements except ohms.
The following C++ code can take 12 and 13-byte ASCII data as well as 3, 5 and 6-byte binary from a 8505/6A and convert it to a 64bit floating point number. The binary data isn't range or calibration corrected yet. I also still need to work out triggering for the binary data. But I should have those figured out shortly and then I can start testing with my meter. I downloaded the community edition of NI LabVIEW so I might try using it to make a program to control 8505/6As. If anyone has suggestions I'd be happy to hear them.
#include <iostream>
#include <inttypes.h>
// TYPE DEFINITIONS
// fixed point data from average mode
typedef union {
struct {
uint32_t f; // fraction bits
uint8_t i :7; // integer bits
bool s :1; // sign bit
};
uint64_t w :40; // 40bit fixed point word
uint8_t b[5];
} fxp40_t; // 40-bit fixed point data bit fields
// fixed point data from normal operation
typedef union {
struct {
uint32_t f :24; // fraction bits
uint8_t i :7; // integer bits
bool s :1; // sign bit
};
uint32_t w; // 32bit fixed point word
uint8_t b[4];
} fxp32_t; // 32-bit fixed point data bit fields
// fixed point data from high speed mode
typedef union {
struct {
uint32_t f :20; // fraction bits
bool i :1; // integer bit
bool e :1; // error bit
bool s :1; // sign bit
};
uint32_t w :22; // 22bit fixed point word
uint8_t b[3];
} fxp22_t; // 22bit fixed point data bit fields
typedef union {
struct {
uint64_t f :63; // mantissa and exponent
bool s :1; // sign
};
double r = 1; // set mantissa bits to 0
} fp64_t; // 64bit floating point bit fields
// GLOBAL VARIABLES
constexpr char LF = 10; // line feed
constexpr char CR = 13; // carriage return
bool meas_err = 0; // flag for DMM reading error
bool data_type = 1; // data type flag: 0 => binary, 1 => ASCII
// FUNCTIONS
// convert fixed point types to 64bit floating point
double fxp_to_fp64(uint64_t f, uint8_t i, bool s) {
fp64_t val;
val.f |= f; // fraction bits
val.r += i - 1; // integer bit / FP exp
val.s = s; // sign bit
return val.r; // unscaled floating point value
}
double binary_to_double(uint8_t b[], const uint8_t& length) {
// high speed mode, 3-byte binary
if (b[length - 1] == 3) {
fxp22_t fxp;
std::copy(&b[length - 1 - b[length - 1]], &b[length - 1], &fxp.b[0]);
if (!fxp.s == fxp.e) // check error bit for measurement error
meas_err = true;
if (fxp.s) // perform two's complement if value is negative
fxp.w = ~fxp.w + 1;
// convert fixed point data to floating point
return fxp_to_fp64(static_cast<uint64_t>(fxp.f) << 32, fxp.i, fxp.s);
}
//normal mode, 5-byte binary
else if (b[length - 1] == 5) {
fxp32_t fxp;
std::copy(&b[length - b[length - 1]], &b[length - 1], &fxp.b[0]);
if (b[length - 6] == 0) // check exponent for measurement error
meas_err = true;
if (fxp.s) { // perform two's complement if value is negative
fxp.w = ~fxp.w + 1;
fxp.s = 1; // retain sign information
}
// convert fixed point data to floating point
return fxp_to_fp64(static_cast<uint64_t>(fxp.f) << 28, fxp.i, fxp.s);
}
// average mode, 6-byte binary
else if (b[length - 1] == 6) {
fxp40_t fxp;
std::copy(&b[length - b[length - 1]], &b[length - 1], &fxp.b[0]);
if (b[length - 7] == 0) // check exponent for measurement error
meas_err = true;
if (fxp.s) { // perform two's complement if value is negative
fxp.w = ~fxp.w + 1;
fxp.s = 1; // retain sign information
}
// convert fixed point data to floating point
return fxp_to_fp64(static_cast<uint64_t>(fxp.f) << 20, fxp.i, fxp.s);
}
// handle mangled binary data
else {
meas_err = true;
return 0;
}
}
double ascii_to_double(uint8_t b[], const uint8_t& length) {
// normal mode 5.5/6.5 digit measurement
if (b[length - 1] == 12) {
char s[13] = {0,0,0,0,0,0,0,0,0,0,0,0,0};
std::copy(&b[0], &b[12], &s[0]);
return std::stod(s);
}
// average mode 6.5/7.5 digit measurement
else if (b[length - 1] == 13) {
char s[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};
std::copy(&b[0], &b[13], &s[0]);
return std::stod(s);
}
// handle CR LF terminations and mangled ASCII data
else {
meas_err = true;
return 0;
}
}
//dummy data for testing
uint8_t b_data[] =
/* 3-byte binary data
{0b11110111, // 1st byte
0b10111111, // 2nd byte
0b11111111}; // 3rd byte
//*/
/* 5-byte binary data
{0b00000011, // 1st byte
0b10000000, // 2nd byte
0b00000000, // 3rd byte
0b00000000, // 4th byte
0b00000001}; // 5th byte
//*/
/* 6-byte binary data
{0b00000011, // 1st byte
0b10000000, // 2nd byte
0b00000000, // 3rd byte
0b00000000, // 4th byte
0b00000000, // 5th byte
0b00000001}; // 6th byte
//*/
//* 12-byte ASCII data
{'+','0','1','0','.','0','0','0','1','E','-','3',CR,LF};
//*/
/* 13-byte ASCII data
{'-','1','0','.','0','0','0','3','3','3','E','+','0',CR,LF};
//*/
/* test terminations for ASCII data
{CR,LF};
//*/
/* mangled ASCII data test
{'-','1','0','.','0',CR,LF};
//*/
double get_reading(void) {
// character buffer, last index stores number of bytes read from DMM
uint8_t b[14];
// read binary data from DMM
if (data_type == 0) {
// store bytes in reverse order, b[sizeof b - 1] counts # of bytes
for (uint8_t &i = b[sizeof b - 1]; i <= sizeof b_data - 1; i++)
b[sizeof b - 2 - i] = b_data[i];
std::cout << (int)b[sizeof b -1] << "-byte binary" << std::endl;
return binary_to_double(b, sizeof b);
}
// read ASCII character data from DMM
else {
// store valid characters in buffer, b[sizeof b - 1] counts # of bytes
for (uint8_t &i = b[sizeof b - 1]; b_data[i]!=CR && b_data[i]!=LF; i++)
b[i] = b_data[i];
std::cout << (int)b[sizeof b -1] << "-byte ASCII" << std::endl;
return ascii_to_double(b, sizeof b);
}
}
int main() {
// set data type: 0 == binary, 1 == ASCII
data_type = 1;
// get reading and convert to 64bit FP
double r = get_reading();
std::cout << "reading error? " << meas_err << std::endl;
printf("FP value: %4.7f \n", r);
}