android13/hardware/rockchip/sensor/st/akm09911/Measure.c

999 lines
26 KiB
C
Executable File

/******************************************************************************
*
* $Id: Measure.c 985 2013-02-08 07:28:29Z miyazaki.hr $
*
* -- Copyright Notice --
*
* Copyright (c) 2004 Asahi Kasei Microdevices Corporation, Japan
* All Rights Reserved.
*
* This software program is the proprietary program of Asahi Kasei Microdevices
* Corporation("AKM") licensed to authorized Licensee under the respective
* agreement between the Licensee and AKM only for use with AKM's electronic
* compass IC.
*
* THIS SOFTWARE IS PROVIDED TO YOU "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABLITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT OF
* THIRD PARTY RIGHTS, AND WE SHALL NOT BE LIABLE FOR ANY LOSSES AND DAMAGES
* WHICH MAY OCCUR THROUGH USE OF THIS SOFTWARE.
*
* -- End Asahi Kasei Microdevices Copyright Notice --
*
******************************************************************************/
#include "AKCommon.h"
#include "AKMD_Driver.h"
#include "DispMessage.h"
#include "FileIO.h"
#include "Measure.h"
#include "misc.h"
#define ACC_ACQ_FLAG_POS ACC_DATA_FLAG
#define MAG_ACQ_FLAG_POS MAG_DATA_FLAG
#define FUSION_ACQ_FLAG_POS FUSION_DATA_FLAG
#define ACC_MES_FLAG_POS 8
#define ACC_INT_FLAG_POS 9
#define MAG_MES_FLAG_POS 10
#define MAG_INT_FLAG_POS 11
#define SETTING_FLAG_POS 12
#define AKMD_MAG_MIN_INTERVAL 10000000 /*!< magnetometer interval */
#define AKMD_ACC_MIN_INTERVAL 10000000 /*!< acceleration interval */
#define AKMD_FUSION_MIN_INTERVAL 10000000 /*!< fusion interval */
#define AKMD_MAG_INTERVAL 50000000 /*!< magnetometer interval */
#define AKMD_ACC_INTERVAL 50000000 /*!< acceleration interval */
#define AKMD_FUSION_INTERVAL 10000000 /*!< fusion interval */
#define AKMD_LOOP_MARGIN 3000000 /*!< Minimum sleep time */
#define AKMD_SETTING_INTERVAL 500000000 /*!< Setting event interval */
static FORM_CLASS* g_form = NULL;
/*!
This function open formation status device.
@return Return 0 on success. Negative value on fail.
*/
static int16 openForm(void)
{
if (g_form != NULL) {
if (g_form->open != NULL) {
return g_form->open();
}
}
// If function is not set, return success.
return 0;
}
/*!
This function close formation status device.
@return None.
*/
static void closeForm(void)
{
if (g_form != NULL) {
if (g_form->close != NULL) {
g_form->close();
}
}
}
/*!
This function check formation status
@return The index of formation.
*/
static int16 checkForm(void)
{
if (g_form != NULL) {
if (g_form->check != NULL) {
return g_form->check();
}
}
// If function is not set, return default value.
return 0;
}
/*!
This function registers the callback function.
@param[in]
*/
void RegisterFormClass(FORM_CLASS* pt)
{
g_form = pt;
}
/*!
Initialize #AKSCPRMS structure. At first, 0 is set to all parameters.
After that, some parameters, which should not be 0, are set to specific
value. Some of initial values can be customized by editing the file
\c "CustomerSpec.h".
@param[out] prms A pointer to #AKSCPRMS structure.
*/
void InitAKSCPRMS(AKSCPRMS* prms)
{
// Set 0 to the AKSCPRMS structure.
memset(prms, 0, sizeof(AKSCPRMS));
// Sensitivity
prms->m_hs.u.x = AKSC_HSENSE_TARGET;
prms->m_hs.u.y = AKSC_HSENSE_TARGET;
prms->m_hs.u.z = AKSC_HSENSE_TARGET;
// HDOE
prms->m_hdst = AKSC_HDST_UNSOLVED;
// (m_hdata is initialized with AKSC_InitDecomp)
prms->m_hnave = CSPEC_HNAVE;
prms->m_dvec.u.x = CSPEC_DVEC_X;
prms->m_dvec.u.y = CSPEC_DVEC_Y;
prms->m_dvec.u.z = CSPEC_DVEC_Z;
}
/*!
Fill #AKSCPRMS structure with default value.
@param[out] prms A pointer to #AKSCPRMS structure.
*/
void SetDefaultPRMS(AKSCPRMS* prms)
{
int16 i;
// Set parameter to HDST, HO, HREF
for (i = 0; i < CSPEC_NUM_FORMATION; i++) {
prms->HSUC_HDST[i] = AKSC_HDST_UNSOLVED;
prms->HSUC_HO[i].u.x = 0;
prms->HSUC_HO[i].u.y = 0;
prms->HSUC_HO[i].u.z = 0;
prms->HFLUCV_HREF[i].u.x = 0;
prms->HFLUCV_HREF[i].u.y = 0;
prms->HFLUCV_HREF[i].u.z = 0;
prms->HSUC_HBASE[i].u.x = 0;
prms->HSUC_HBASE[i].u.y = 0;
prms->HSUC_HBASE[i].u.z = 0;
}
}
/*!
Get interval from device driver. This function will not resolve dependencies.
Dependencies will be resolved in Sensor HAL.
@param[out] acc_mes Accelerometer measurement timing.
@param[out] mag_mes Magnetometer measurement timing.
@param[out] acc_acq Accelerometer acquisition timing.
@param[out] mag_acq Magnetometer acquisition timing.
@param[out] fusion_acq Orientation sensor acquisition timing.
@param[out] hdoe_interval HDOE decimator.
*/
int16 GetInterval(
AKMD_LOOP_TIME* acc_mes,
AKMD_LOOP_TIME* mag_mes,
AKMD_LOOP_TIME* acc_acq,
AKMD_LOOP_TIME* mag_acq,
AKMD_LOOP_TIME* fusion_acq,
int16* hdoe_dec)
{
/* Accelerometer, Magnetometer, Orientation */
/* Delay is in nano second unit. */
/* Negative value means the sensor is disabled.*/
int64_t delay[AKM_NUM_SENSORS];
int64_t acc_last_interval = 0;
if (AKD_GetDelay(delay) != AKD_SUCCESS) {
return AKRET_PROC_FAIL;
}
AKMDEBUG(AKMDBG_GETINTERVAL,"delay=%lld,%lld,%lld\n",
delay[0], delay[1], delay[2]);
#ifdef AKMD_ACC_EXTERNAL
/* Always disabled */
delay[0] = -1;
#else
/* Accelerometer's interval limit */
if ((0 <= delay[0]) && (delay[0] <= AKMD_ACC_MIN_INTERVAL)) {
delay[0] = AKMD_ACC_MIN_INTERVAL;
}
#endif
/* Magnetmeter's frequency should be discrete value */
if (0 <= delay[1]) {
GetHDOEDecimator(&(delay[1]), hdoe_dec);
}
/* Fusion sensor's interval limit */
if ((0 <= delay[2]) && (delay[2] <= AKMD_FUSION_MIN_INTERVAL)) {
delay[2] = AKMD_FUSION_MIN_INTERVAL;
}
if ((delay[0] != acc_acq->interval) ||
(delay[1] != mag_acq->interval) ||
(delay[2] != fusion_acq->interval)) {
/* reserve previous value */
acc_last_interval = acc_mes->interval;
/* Copy new value */
acc_acq->duration = acc_acq->interval = delay[0];
mag_acq->duration = mag_acq->interval = delay[1];
fusion_acq->duration = fusion_acq->interval = delay[2];
if (fusion_acq->interval < 0) {
/* NO relation between orientation sensor */
acc_mes->interval = acc_acq->interval;
mag_mes->interval = mag_acq->interval;
acc_mes->duration = 0;
mag_mes->duration = 0;
} else {
/* Solve dependency */
if ((acc_acq->interval >= 0) &&
(acc_acq->interval < fusion_acq->interval)) {
acc_mes->interval = acc_acq->interval;
acc_mes->duration = 0;
} else {
acc_mes->interval = fusion_acq->interval;
acc_mes->duration = 0;
}
if ((mag_acq->interval >= 0) &&
(mag_acq->interval < fusion_acq->interval)) {
mag_mes->interval = mag_acq->interval;
mag_mes->duration = 0;
} else {
mag_mes->interval = fusion_acq->interval;
mag_mes->duration = 0;
}
}
if (acc_last_interval != acc_mes->interval) {
if (acc_mes->interval >= 0) {
/* Acc is enabled */
if (AKD_AccSetEnable(AKD_ENABLE) != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
/* Then set interval */
if (AKD_AccSetDelay(acc_acq->interval) != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
} else {
/* Acc is disabled */
if (AKD_AccSetEnable(AKD_DISABLE) != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
}
}
AKMDEBUG(AKMDBG_GETINTERVAL,
"%s:\n"
" AcqInterval(M,A,Fusion)=%lld,%lld,%lld\n"
" MesInterval(M,A)=%lld,%lld\n",
__FUNCTION__,
mag_acq->interval, acc_acq->interval, fusion_acq->interval,
mag_mes->interval, acc_mes->interval);
}
return AKRET_PROC_SUCCEED;
}
/*!
Calculate loop duration
@return If it is time to fire the event, the return value is 1, otherwise 0.
@param[in,out] tm An event.
@param[in] execTime The time to execute main loop for one time.
@param[in,out] minDuration The minimum sleep time in all events.
*/
int SetLoopTime(
AKMD_LOOP_TIME* tm,
int64_t execTime,
int64_t* minDuration)
{
int ret = 0;
if (tm->interval >= 0) {
tm->duration -= execTime;
if (tm->duration <= AKMD_LOOP_MARGIN) {
tm->duration = tm->interval;
ret = 1;
} else if (tm->duration < *minDuration) {
*minDuration = tm->duration;
}
}
return ret;
}
/*!
Read hard coded value (Fuse ROM) from AKM E-Compass. Then set the read value
to calculation parameter.
@return If parameters are read successfully, the return value is
#AKRET_PROC_SUCCEED. Otherwise the return value is #AKRET_PROC_FAIL. No
error code is reserved to show which operation has failed.
@param[out] prms A pointer to #AKSCPRMS structure.
*/
int16 ReadFUSEROM(AKSCPRMS* prms)
{
BYTE info[AKM_SENSOR_INFO_SIZE];
BYTE conf[AKM_SENSOR_CONF_SIZE];
// Get information
if (AKD_GetSensorInfo(info) != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
// Get configuration
if (AKD_GetSensorConf(conf) != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
prms->m_asa.u.x = (int16)conf[0];
prms->m_asa.u.y = (int16)conf[1];
prms->m_asa.u.z = (int16)conf[2];
AKMDEBUG(AKMDBG_DEBUG, "%s: asa(dec)=%d,%d,%d\n", __FUNCTION__,
prms->m_asa.u.x, prms->m_asa.u.y, prms->m_asa.u.z);
// Set keywords for SmartCompassLibrary certification
prms->m_key[0] = AKSC_GetVersion_Device();
prms->m_key[1] = (int16)(((uint16)info[1] << 8) | info[0]);
prms->m_key[2] = (int16)conf[0];
prms->m_key[3] = (int16)conf[1];
prms->m_key[4] = (int16)conf[2];
strncpy((char *)prms->m_licenser, CSPEC_CI_LICENSER, AKSC_CI_MAX_CHARSIZE);
strncpy((char *)prms->m_licensee, CSPEC_CI_LICENSEE, AKSC_CI_MAX_CHARSIZE);
AKMDEBUG(AKMDBG_DEBUG, "%s: key=%d, licenser=%s, licensee=%s\n",
__FUNCTION__, prms->m_key[1], prms->m_licenser, prms->m_licensee);
return AKRET_PROC_SUCCEED;
}
/*!
Set initial values for SmartCompass library.
@return If parameters are read successfully, the return value is
#AKRET_PROC_SUCCEED. Otherwise the return value is #AKRET_PROC_FAIL. No
error code is reserved to show which operation has failed.
@param[in,out] prms A pointer to a #AKSCPRMS structure.
*/
int16 Init_Measure(AKSCPRMS* prms)
{
// Reset device.
if (AKD_Reset() != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
prms->m_form = checkForm();
// Restore the value when succeeding in estimating of HOffset.
prms->m_ho = prms->HSUC_HO[prms->m_form];
prms->m_ho32.u.x = (int32)prms->HSUC_HO[prms->m_form].u.x;
prms->m_ho32.u.y = (int32)prms->HSUC_HO[prms->m_form].u.y;
prms->m_ho32.u.z = (int32)prms->HSUC_HO[prms->m_form].u.z;
prms->m_hdst = prms->HSUC_HDST[prms->m_form];
prms->m_hbase = prms->HSUC_HBASE[prms->m_form];
// Initialize the decompose parameters
AKSC_InitDecomp(prms->m_hdata);
// Initialize HDOE parameters
AKSC_InitHDOEProcPrmsS3(
&prms->m_hdoev,
1,
&prms->m_ho,
prms->m_hdst
);
AKSC_InitHFlucCheck(
&(prms->m_hflucv),
&(prms->HFLUCV_HREF[prms->m_form]),
HFLUCV_TH
);
// Reset counter
prms->m_cntSuspend = 0;
prms->m_callcnt = 0;
return AKRET_PROC_SUCCEED;
}
/*!
This is the main routine of measurement.
@param[in,out] prms A pointer to a #AKSCPRMS structure.
*/
void MeasureSNGLoop(AKSCPRMS* prms)
{
BYTE i2cData[AKM_SENSOR_DATA_SIZE];
int16 bData[AKM_SENSOR_DATA_SIZE]; // Measuring block data
int16 adata[3];
int16 ret;
int16 i;
int16 hdoe_interval = 1;
/* Acceleration interval */
AKMD_LOOP_TIME acc_acq = { -1, 0 };
/* Magnetic field interval */
AKMD_LOOP_TIME mag_acq = { -1, 0 };
/* Orientation interval */
AKMD_LOOP_TIME fusion_acq = { -1, 0 };
/* Magnetic acquisition interval */
AKMD_LOOP_TIME mag_mes = { -1, 0 };
/* Acceleration acquisition interval */
AKMD_LOOP_TIME acc_mes = { -1, 0 };
/* Magnetic measurement interval */
AKMD_LOOP_TIME mag_int = { AKM_MEASUREMENT_TIME_NS, 0 };
/* Setting interval */
AKMD_LOOP_TIME setting = { AKMD_SETTING_INTERVAL, 0 };
/* 0x0001: Acceleration execute flag (data output) */
/* 0x0002: Magnetic execute flag (data output) */
/* 0x0004: Fusion execute flag (data output) */
/* 0x0100: Magnetic measurement flag */
/* 0x0200: Magnetic interrupt flag */
/* 0x0400: Acceleration measurement flag */
/* 0x0800: Acceleration interrupt flag */
/* 0x0400: Setting execute flag */
uint16 exec_flags;
struct timespec currTime = { 0, 0 }; /* Current time */
struct timespec lastTime = { 0, 0 }; /* Previous time */
int64_t execTime; /* Time between two points */
int64_t minVal; /* The minimum duration to the next event */
int measuring = 0; /* The value is 1, if while measuring. */
if (openForm() < 0) {
AKMERROR;
return;
}
/* Initialize */
if (Init_Measure(prms) != AKRET_PROC_SUCCEED) {
goto MEASURE_SNG_END;
}
/* Get initial interva */
if (GetInterval(
&acc_mes, &mag_mes,
&acc_acq, &mag_acq, &fusion_acq,
&hdoe_interval) != AKRET_PROC_SUCCEED) {
AKMERROR;
goto MEASURE_SNG_END;
}
/* Beginning time */
if (clock_gettime(CLOCK_MONOTONIC, &currTime) < 0) {
AKMERROR;
goto MEASURE_SNG_END;
}
//TODO: Define stop flag
while (g_stopRequest != 1) {
exec_flags = 0;
minVal = 1000000000; /*1sec*/
/* Copy the last time */
lastTime = currTime;
/* Get current time */
if (clock_gettime(CLOCK_MONOTONIC, &currTime) < 0) {
AKMERROR;
break;
}
/* Calculate the difference */
execTime = CalcDuration(&currTime, &lastTime);
AKMDEBUG(AKMDBG_EXECTIME,
"Executing(%6.2f)\n", (double)execTime / 1000000.0);
/* Subtract the differential time from each event.
If subtracted value is negative turn event flag on. */
exec_flags |= (SetLoopTime(&setting, execTime, &minVal)
<< (SETTING_FLAG_POS));
exec_flags |= (SetLoopTime(&acc_acq, execTime, &minVal)
<< (ACC_ACQ_FLAG_POS));
exec_flags |= (SetLoopTime(&mag_acq, execTime, &minVal)
<< (MAG_ACQ_FLAG_POS));
exec_flags |= (SetLoopTime(&fusion_acq, execTime, &minVal)
<< (FUSION_ACQ_FLAG_POS));
exec_flags |= (SetLoopTime(&acc_mes, execTime, &minVal)
<< (ACC_MES_FLAG_POS));
/* Magnetometer needs special care. While the device is
under measuring, measurement start flag should not be turned on.*/
if (mag_mes.interval >= 0) {
mag_mes.duration -= execTime;
if (!measuring) {
/* Not measuring */
if (mag_mes.duration <= AKMD_LOOP_MARGIN) {
exec_flags |= (1 << (MAG_MES_FLAG_POS));
} else if (mag_mes.duration < minVal) {
minVal = mag_mes.duration;
}
} else {
/* While measuring */
mag_int.duration -= execTime;
/* NO_MARGIN! */
if (mag_int.duration <= 0) {
exec_flags |= (1 << (MAG_INT_FLAG_POS));
} else if (mag_int.duration < minVal) {
minVal = mag_int.duration;
}
}
}
/* If all flag is off, go sleep */
if (exec_flags == 0) {
AKMDEBUG(AKMDBG_EXECTIME, "Sleeping(%6.2f)...\n",
(double)minVal / 1000000.0);
if (minVal > 0) {
struct timespec doze = { 0, 0 };
doze = int64_to_timespec(minVal);
nanosleep(&doze, NULL);
}
} else {
AKMDEBUG(AKMDBG_EXECTIME, "ExecFlags=0x%04X\n", exec_flags);
if (exec_flags & (1 << (MAG_MES_FLAG_POS))) {
/* Set to SNG measurement pattern (Set CNTL register) */
if (AKD_SetMode(AKM_MODE_SNG_MEASURE) != AKD_SUCCESS) {
AKMERROR;
} else {
mag_mes.duration = mag_mes.interval;
mag_int.duration = mag_int.interval;
measuring = 1;
}
}
if (exec_flags & (1 << (MAG_INT_FLAG_POS))) {
/* Get magnetometer measurement data */
if (AKD_GetMagneticData(i2cData) != AKD_SUCCESS) {
AKMERROR;
// Reset driver
AKD_Reset();
// Unset flag
exec_flags &= ~(1 << (MAG_INT_FLAG_POS));
} else {
// Copy to local variable
for (i=0; i<AKM_SENSOR_DATA_SIZE; i++) {
bData[i] = i2cData[i];
}
ret = GetMagneticVector(
bData,
prms,
checkForm(),
hdoe_interval);
// Check the return value
if ((ret != AKRET_PROC_SUCCEED) && (ret != AKRET_FORMATION_CHANGED)) {
ALOGE("GetMagneticVector has failed (0x%04X).\n", ret);
}
AKMDEBUG(AKMDBG_VECTOR, "mag(dec)=%6d,%6d,%6d\n",
prms->m_hvec.u.x, prms->m_hvec.u.y, prms->m_hvec.u.z);
}
measuring = 0;
}
if (exec_flags & (1 << (ACC_MES_FLAG_POS))) {
/* Get accelerometer data */
if (AKD_GetAccelerationData(adata) != AKD_SUCCESS) {
AKMERROR;
break;
}
AKD_GetAccelerationVector(adata, prms->m_AO.v, prms->m_avec.v);
AKMDEBUG(AKMDBG_VECTOR, "acc(dec)=%6d,%6d,%6d\n",
prms->m_avec.u.x, prms->m_avec.u.y, prms->m_avec.u.z);
}
if (exec_flags & (1 << (FUSION_ACQ_FLAG_POS))) {
if (CalcDirection(prms) != AKRET_PROC_SUCCEED) {
AKMERROR;
}
}
/* Calculate direction angle */
if (exec_flags & 0x0F) {
/* If any ACQ flag is on, report the data to device driver */
Disp_MeasurementResultHook(prms, (uint16)(exec_flags & 0x0F));
}
if (exec_flags & (1 << (SETTING_FLAG_POS))) {
/* Get measurement interval from device driver */
GetInterval(
&acc_mes, &mag_mes,
&acc_acq, &mag_acq, &fusion_acq,
&hdoe_interval);
}
}
}
MEASURE_SNG_END:
// Disable all sensors
if (AKD_SetMode(AKM_MODE_POWERDOWN) != AKD_SUCCESS) {AKMERROR;}
if (AKD_AccSetEnable(AKD_DISABLE) != AKD_SUCCESS) {AKMERROR;}
closeForm();
}
/*!
SmartCompass main calculation routine. This function will be processed
when INT pin event is occurred.
@retval AKRET_
@param[in] bData An array of register values which holds,
ST1, HXL, HXH, HYL, HYH, HZL, HZH and ST2 value respectively.
@param[in,out] prms A pointer to a #AKSCPRMS structure.
@param[in] curForm The index of hardware position which represents the
index when this function is called.
@param[in] hDecimator HDOE will execute once while this function is called
this number of times.
*/
int16 GetMagneticVector(
const int16 bData[],
AKSCPRMS* prms,
const int16 curForm,
const int16 hDecimator)
{
const int16vec hrefZero = {{0, 0, 0}};
int16vec have, hvec;
int16 temperature, dor, derr, hofl, cb, dc;
int32vec preHbase;
int16 overflow;
int16 hfluc;
int16 hdSucc;
int16 aksc_ret;
int16 ret;
have.u.x = 0;
have.u.y = 0;
have.u.z = 0;
temperature = 0;
dor = 0;
derr = 0;
hofl = 0;
cb = 0;
dc = 0;
preHbase = prms->m_hbase;
overflow = 0;
ret = AKRET_PROC_SUCCEED;
// Subtract the formation suspend counter
if (prms->m_cntSuspend > 0) {
prms->m_cntSuspend--;
// Check the suspend counter
if (prms->m_cntSuspend == 0) {
// Restore the value when succeeding in estimating of HOffset.
prms->m_ho = prms->HSUC_HO[prms->m_form];
prms->m_ho32.u.x = (int32)prms->HSUC_HO[prms->m_form].u.x;
prms->m_ho32.u.y = (int32)prms->HSUC_HO[prms->m_form].u.y;
prms->m_ho32.u.z = (int32)prms->HSUC_HO[prms->m_form].u.z;
prms->m_hdst = prms->HSUC_HDST[prms->m_form];
prms->m_hbase = prms->HSUC_HBASE[prms->m_form];
// Initialize the decompose parameters
AKSC_InitDecomp(prms->m_hdata);
// Initialize HDOE parameters
AKSC_InitHDOEProcPrmsS3(
&prms->m_hdoev,
1,
&prms->m_ho,
prms->m_hdst
);
// Initialize HFlucCheck parameters
AKSC_InitHFlucCheck(
&(prms->m_hflucv),
&(prms->HFLUCV_HREF[prms->m_form]),
HFLUCV_TH
);
}
}
// Decompose one block data into each Magnetic sensor's data
aksc_ret = AKSC_DecompS3(
AKSC_GetVersion_Device(),
bData,
prms->m_hnave,
&prms->m_asa,
prms->m_pdcptr,
prms->m_hdata,
&prms->m_hbase,
&prms->m_hn,
&have,
&temperature,
&dor,
&derr,
&hofl,
&cb,
&dc
);
AKM_LOG("%s: ST1, HXH&HXL, HYH&HYL, HZH&HZL, ST2,"
" hdata[0].u.x, hdata[0].u.y, hdata[0].u.z,"
" asax, asay, asaz ="
" %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
__FUNCTION__,
bData[0],
(int16)(((uint16)bData[2])<<8|bData[1]),
(int16)(((uint16)bData[4])<<8|bData[3]),
(int16)(((uint16)bData[6])<<8|bData[5]), bData[8],
prms->m_hdata[0].u.x, prms->m_hdata[0].u.y, prms->m_hdata[0].u.z,
prms->m_asa.u.x, prms->m_asa.u.y, prms->m_asa.u.z);
if (aksc_ret == 0) {
AKMERROR;
AKMDEBUG(AKMDBG_DUMP,
"AKSC_DecompS3 failed.\n"
" ST1=0x%02X, ST2=0x%02X\n"
" XYZ(HEX)=%02X,%02X,%02X,%02X,%02X,%02X\n"
" asa(dec)=%d,%d,%d\n"
" pdc(addr)=0x%p\n"
" hbase(dec)=%ld,%ld,%ld\n",
bData[0], bData[8],
bData[1], bData[2], bData[3], bData[4], bData[5], bData[6],
prms->m_asa.u.x, prms->m_asa.u.y, prms->m_asa.u.z,
prms->m_pdcptr,
prms->m_hbase.u.x, prms->m_hbase.u.y, prms->m_hbase.u.z);
return AKRET_PROC_FAIL;
}
// Check the formation change
if (prms->m_form != curForm) {
prms->m_form = curForm;
prms->m_cntSuspend = CSPEC_CNTSUSPEND_SNG;
prms->m_callcnt = 0;
ret |= AKRET_FORMATION_CHANGED;
return ret;
}
// Check derr
if (derr == 1) {
ret |= AKRET_DATA_READERROR;
return ret;
}
// Check hofl
if (hofl == 1) {
if (prms->m_cntSuspend <= 0) {
// Set a HDOE level as "HDST_UNSOLVED"
AKSC_SetHDOELevel(
&prms->m_hdoev,
&prms->m_ho,
AKSC_HDST_UNSOLVED,
1
);
prms->m_hdst = AKSC_HDST_UNSOLVED;
}
ret |= AKRET_DATA_OVERFLOW;
return ret;
}
// Check hbase
if (cb == 1) {
// Translate HOffset
AKSC_TransByHbase(
&(preHbase),
&(prms->m_hbase),
&(prms->m_ho),
&(prms->m_ho32),
&overflow
);
if (overflow == 1) {
ret |= AKRET_OFFSET_OVERFLOW;
}
// Set hflucv.href to 0
AKSC_InitHFlucCheck(
&(prms->m_hflucv),
&(hrefZero),
HFLUCV_TH
);
if (prms->m_cntSuspend <= 0) {
AKSC_SetHDOELevel(
&prms->m_hdoev,
&prms->m_ho,
AKSC_HDST_UNSOLVED,
1
);
prms->m_hdst = AKSC_HDST_UNSOLVED;
}
ret |= AKRET_HBASE_CHANGED;
return ret;
}
if (prms->m_cntSuspend <= 0) {
// Detect a fluctuation of magnetic field.
hfluc = AKSC_HFlucCheck(&(prms->m_hflucv), &(prms->m_hdata[0]));
if (hfluc == 1) {
// Set a HDOE level as "HDST_UNSOLVED"
AKSC_SetHDOELevel(
&prms->m_hdoev,
&prms->m_ho,
AKSC_HDST_UNSOLVED,
1
);
prms->m_hdst = AKSC_HDST_UNSOLVED;
ret |= AKRET_HFLUC_OCCURRED;
return ret;
}
else {
prms->m_callcnt--;
if (prms->m_callcnt <= 0) {
//Calculate Magnetic sensor's offset by DOE
hdSucc = AKSC_HDOEProcessS3(
prms->m_licenser,
prms->m_licensee,
prms->m_key,
&prms->m_hdoev,
prms->m_hdata,
prms->m_hn,
&prms->m_ho,
&prms->m_hdst
);
if (hdSucc == AKSC_CERTIFICATION_DENIED) {
AKMERROR;
return AKRET_PROC_FAIL;
}
if (hdSucc > 0) {
prms->HSUC_HO[prms->m_form] = prms->m_ho;
prms->m_ho32.u.x = (int32)prms->m_ho.u.x;
prms->m_ho32.u.y = (int32)prms->m_ho.u.y;
prms->m_ho32.u.z = (int32)prms->m_ho.u.z;
prms->HSUC_HDST[prms->m_form] = prms->m_hdst;
prms->HFLUCV_HREF[prms->m_form] = prms->m_hflucv.href;
prms->HSUC_HBASE[prms->m_form] = prms->m_hbase;
}
//Set decimator counter
prms->m_callcnt = hDecimator;
}
}
}
// Subtract offset and normalize magnetic field vector.
aksc_ret = AKSC_VNorm(
&have,
&prms->m_ho,
&prms->m_hs,
AKSC_HSENSE_TARGET,
&hvec
);
if (aksc_ret == 0) {
AKMERROR;
AKMDEBUG(AKMDBG_DUMP,
"AKSC_VNorm failed.\n"
" have=%6d,%6d,%6d ho=%6d,%6d,%6d hs=%6d,%6d,%6d\n",
have.u.x, have.u.y, have.u.z,
prms->m_ho.u.x, prms->m_ho.u.y, prms->m_ho.u.z,
prms->m_hs.u.x, prms->m_hs.u.y, prms->m_hs.u.z);
ret |= AKRET_VNORM_ERROR;
return ret;
}
// hvec is updated only when VNorm function is succeeded.
prms->m_hvec = hvec;
//Convert layout from sensor to Android by using PAT number.
// Magnetometer
ConvertCoordinate(prms->m_hlayout, &prms->m_hvec);
return AKRET_PROC_SUCCEED;
}
/*!
Calculate Yaw, Pitch, Roll angle.
m_hvec, m_avec and m_gvec should be Android coordination.
@return Always return #AKRET_PROC_SUCCEED.
@param[in,out] prms A pointer to a #AKSCPRMS structure.
*/
int16 CalcDirection(AKSCPRMS* prms)
{
/* Conversion matrix from Android to SmartCompass coordination */
int16 preThe;
const I16MATRIX hlayout = {{
0, 1, 0,
-1, 0, 0,
0, 0, 1}};
const I16MATRIX alayout = {{
0,-1, 0,
1, 0, 0,
0, 0,-1}};
preThe = prms->m_theta;
prms->m_d6dRet = AKSC_DirectionS3(
prms->m_licenser,
prms->m_licensee,
prms->m_key,
&prms->m_hvec,
&prms->m_avec,
&prms->m_dvec,
&hlayout,
&alayout,
&prms->m_theta,
&prms->m_delta,
&prms->m_hr,
&prms->m_hrhoriz,
&prms->m_ar,
&prms->m_phi180,
&prms->m_phi90,
&prms->m_eta180,
&prms->m_eta90,
&prms->m_mat,
&prms->m_quat);
prms->m_theta = AKSC_ThetaFilter(
prms->m_theta,
preThe,
THETAFILTER_SCALE);
if (prms->m_d6dRet == AKSC_CERTIFICATION_DENIED) {
AKMERROR;
return AKRET_PROC_FAIL;
}
if (prms->m_d6dRet != 3) {
AKMDEBUG(AKMDBG_DUMP,
"AKSC_Direction6D failed (0x%02x).\n"
" hvec=%d,%d,%d avec=%d,%d,%d dvec=%d,%d,%d\n",
prms->m_d6dRet,
prms->m_hvec.u.x, prms->m_hvec.u.y, prms->m_hvec.u.z,
prms->m_avec.u.x, prms->m_avec.u.y, prms->m_avec.u.z,
prms->m_dvec.u.x, prms->m_dvec.u.y, prms->m_dvec.u.z);
}
/* Convert Yaw, Pitch, Roll angle to Android coordinate system */
/* Actually, only Roll angle is opposite */
if (prms->m_d6dRet & 0x02) {
prms->m_eta180 = -prms->m_eta180;
prms->m_eta90 = -prms->m_eta90;
AKMDEBUG(AKMDBG_D6D, "AKSC_Direction6D (0x%02x):\n"
" Yaw, Pitch, Roll=%6.1f,%6.1f,%6.1f\n",
prms->m_d6dRet,
DISP_CONV_Q6F(prms->m_theta),
DISP_CONV_Q6F(prms->m_phi180),
DISP_CONV_Q6F(prms->m_eta90));
}
return AKRET_PROC_SUCCEED;
}
int16 SimpleCalibration(AKSCPRMS* prms)
{
/* Boot up device */
if (AKD_AccSetEnable(AKD_ENABLE) != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
if (AKD_AccSetDelay(AKMD_ACC_INTERVAL) != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
/* Wait for a while until device boot up */
msleep(100);
AKD_GetAccelerationOffset(prms->m_AO.v);
if (AKD_AccSetEnable(AKD_DISABLE) != AKD_SUCCESS) {
AKMERROR;
return AKRET_PROC_FAIL;
}
return AKRET_PROC_SUCCEED;
}