- NGIMU의 Data Sheet 분석 후 작성
- MGIMU의 모든 통신은 OSC로 Encode된다
- Encode : 어떤 정보를 정해진 규칙에 따라 부호화/암호화 하는 것
- OSC (Open Sound Control) : 사운드 관련 퍼포먼스 및 연주 정보 데이터 전송을 위해 MIDI 이후에 개발된 플렛폼
- Network를 이용한 protocol이며 UDP를 사용한다
- TCP(Transmission Control Protocol)/IP : Data 통신 시 확인 msg를 받은 후 전송
- UDP(User Datagram Protocol) : 확인 과정 없이 단방향으로 data 전송
- UDP를 통해 OSC를 사용할 경우, 사용자는 message 수신/송신할 port를 각각 정하고 Third Party device에는 그 값들이 반전되어 들어간다
- Ex) Port 8000 for Receiving(RX), Port 8001 for Transmitting(TX) : Third Party device는 8000 port에 data를 transmit하고 8001 port에서 receive
- USB, Serial이나 SD 카드에 기록된 data set은 SLIP packet으로 OSC Encode 됨
- OSC 관련 내용
- OSC Messgae는 network를 통해 전송되므로 message를 보낼 곳과 받을 곳을 정의해야 함
- 통신 전 미리 정의해야 할 내용들은 아래와 같다.
- IP Address : Message를 보낼 장치의 IP address. 한 SW에서 다른 computer로 data를 보내자 하면 localhost IP address(127.0.0.1)를 사용하면 된다
- 127.0.0.1은 localhost 또는 loopback 주소로써, network상에서 자신의 컴퓨터를 나타내는 가상적인 주소이다. 즉 자신에게 다시 network 입력이 들어오게 한다
- 보통 Application test나 같은 computer 내에서 다른 Application끼리 통신하기 위해 사용한다
- Port : Message를 Send/Transmit 할 port number
- Address Pattern : 보통 URL과 비슷한 형식을 가짐. 같은 port에서 나오는 서로 다른 message들을 구분하기 위해 사용
- ex) /sensor/raw
- Actual Message : 실제로 전송/수신하는 message data. Data type을 정의해줘야 함
- 통신 data는 아래처럼 /LABEL + data의 형태로 이뤄져있음. LABEL은 data 구분용
/sensors/raw/x 0.12354
/sensors/filtered/x 0.2
/sensors/linear/x 0.23
/magnitueds/gyro/ 0.132
- OSC 통신 구현 과정
- 장치로 전송되는 OSC Message는 Interchangeable한 Numerical Argument Types (int32, float32, int64, OSC time tag, 64-bit double, character, boolean, nil, infitnitum)과 blob, string argument type을 사용한다
- 장치로 전송되는 OSC address pattern은 '?', '*', '[]', '{}'와 같은 특수 문자를 포함하지 않는다
- OSC Message는 OSC bundle을 통해 전송되나 message scheduling은 무시된다
- Bundle : Map 형태로 구현된 data의 묶음
1) 장치로부터의 Data
- 장치로부터의 data는 single OSC Messgae를 같는 timestamped OSC bundle 형태로 전송된다
- Button, Auxiliary serial, Serial Message를 제외한 모든 Message들은 정해진 baud rate데로 지속적으로 전송된다
- OSC bundle의 timestamp는 OSC time tag로, 총 64bit-fixed-point number이다
- 첫 32bits는 1900년 1월 1일 00:00부터의 초, 뒤의 32bits는 초의 소숫점을 200 picoseconds(10^-12) 까지의 정밀도로 나타낸다
- Internet NTP timestamp를 사용한다
- Fixed Point Number : 소수부의 자릿수를 정하여 고정된 자릿수의 소수를 표현하는 방식
- OSC time tag는 먼저 64bits unsigned integer로 변환 후 2^32로 나누는 방식을 통해 10진수로 변환 가능하다
- 정밀도의 부족으로 인한 Error를 방지하기 위해 double-precision floating-point type(float 64)을 사용하여 계산한다
- OSC Send
- 수신받을 컴퓨터의 IP Adress, Port Number 설정 후 Label을 붙여 송신
- Ex) 택배 배달
- IP Address : 받을 집의 주소
- Port : 받을 사람의 방 호수
- Label : 보낸 사람의 이름
- 위 내용들을 적어 OSC라는 배달부를 통해 전달
- OSC Receive
- 송신한 computer에서 보낸 Port를 설정해 개방
- 택배 도착 후 방문(Port)를 열고 해당 message 취득
- 해당 센서의 source code에서 C++17부터 도입된 <filesystem>을 사용하므로 compiler version을 C++17을 지원하는 compiler로 upgrade 하거나 C++17을 사용하게 되어있는지 확인
$ gcc --version
-> gcc-7 (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
- gcc 버전 확인
- gcc 버전이 C++17을 지원하지 않는 버전이면 upgrade
$ sudo apt install gcc-8 g++-8
- 최신 버전의 gcc 설치
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 700 --slave /usr/bin/g++ g++ /usr/bin/g++-7
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8
- 최신 버전의 gcc를 defalut로 설정
$ gcc --version
-> gcc (Ubuntu 8.4.0-1ubuntu1~18.04) 8.4.0
- 설정된 gcc 버전 확인
- Source Code 분석
https://github.com/dinimar/ros-ngimu/tree/master
1. NgimuReceive.cc 분석 : NGIMU로부터의 message들을 receive 하기 위한 module
- Include 부분
// Includes
#include "NgimuReceive.h"
#include <stddef.h>
#include <stdio.h> // snprintf
- stddef.h : 아래의 일반적으로 사용되는 pointer, variable, type을 선언
- ptrdiff_t : 두 pointer 차이 type에 대한 typedef
- size_t : sizeof에서 return 하는 값 type에 대한 typedef
- wchar_t : wide character constant에 대한 typedef
- Inlcude된 NgimuReceive.h : NGIMU의 각 Category() 별 data들에 대한 structure를 정의. 관련 함수들 선언
- /sensors
- /quaternion
- /euler
- /temperature
// NgimuReceive.h
#ifndef NGIMU_RECEIVE_H
#define NGIMU_RECEIVE_H
#ifdef __cplusplus
extern "C" {
#endif
//------------------------------------------------------------------------------
// Includes
#include "Osc99/Osc99.h"
//------------------------------------------------------------------------------
// Definitions
/**
* @brief Timestamp and argument values for "/sensors" message.
* Data about the sensor values
*/
typedef struct {
OscTimeTag timestamp;
float gyroscopeX;
float gyroscopeY;
float gyroscopeZ;
float accelerometerX;
float accelerometerY;
float accelerometerZ;
float magnetometerX;
float magnetometerY;
float magnetometerZ;
float barometer;
} NgimuSensors;
/**
* @brief Timestamp and argument values for "/quaternion" message.
* Quaternion output of the on-board AHRS algorithm describing the
* orientation of the device relative to the Earth (NWU convention)
*/
typedef struct {
OscTimeTag timestamp;
float w;
float x;
float y;
float z;
} NgimuQuaternion;
/**
* @brief Timestamp and argument values for "/euler" message.
* Eluer angle output of the on-board AHRS as same as quaternion
*/
typedef struct {
OscTimeTag timestamp;
float roll;
float pitch;
float yaw;
} NgimuEuler;
/**
* @brief Timestamp and argument values for "/temperature" message.
* Temperature measurement data of the divice`s on-board temperature sensor
*/
typedef struct {
OscTimeTag timestamp;
float temp1;
float temp2;
} NgimuTemperature;
//------------------------------------------------------------------------------
// Function prototypes
void NgimuReceiveInitialise();
void NgimuReceiveSetReceiveErrorCallback(void (*newReceiveErrorCallback)(const char* const errorMessage));
void NgimuReceiveSetSensorsCallback(void (*newSensorsCallback)(const NgimuSensors ngimuSensors));
void NgimuReceiveSetQuaternionCallback(void (*newQuaternionCallback)(const NgimuQuaternion ngimuQuaternion));
void NgimuReceiveSetEulerCallback(void (*newEulerCallback)(const NgimuEuler ngimuEuler));
void NgimuReceiveSetTemperatureCallback(void (*newTemperatureCallback)(const NgimuTemperature ngimuEuler));
void NgimuReceiveProcessSerialByte(const char byte);
void NgimuReceiveProcessUdpPacket(const char * const source, const size_t sourceSize);
#ifdef __cplusplus
}
#endif
#endif
//------------------------------------------------------------------------------
// End of file
// NgimuReceive.h
#ifdef __cplusplus
extern "C" {
#endif
.....
.....
.....
#ifdef __cplusplus
}
#endif
- extern "C" :
// NgimuReceive.h
// Includes
#include "Osc99/Osc99.h"
- OSC99 : Embedded system과 함께 사용하도록 개발된 portable ANSI C99 호환 OSC library
/**
* @file Osc99.h
* @author Seb Madgwick
* @brief Main header file for the library. This is the only file that needs to
* be included when using the library.
*
* OSC99 is a portable ANSI C99 compliant OSC library developed for use with
* embedded systems. OSC99 implements the OSC 1.0 specification including all
* optional argument types. The library also includes a SLIP module for
* encoding and decoding OSC packets via unframed protocols such as UART/serial
* as required by the OSC the 1.1 specification.
*
* The following definitions may be modified in OscCommon.h as required by the
* user application: LITTLE_ENDIAN_PLATFORM, MAX_TRANSPORT_SIZE,
* OSC_ERROR_MESSAGES_ENABLED.
*/
#ifndef OSC99_H
#define OSC99_H
//------------------------------------------------------------------------------
// Includes
#ifdef __cplusplus
extern "C" {
#endif
#include "OscAddress.h"
#include "OscError.h"
#include "OscPacket.h"
#include "OscSlip.h"
#ifdef __cplusplus
}
#endif
#endif
//------------------------------------------------------------------------------
// End of file
// NgimuReceive.h
//------------------------------------------------------------------------------
// Definitions
/**
* @brief Timestamp and argument values for "/sensors" message.
* Data about the sensor values
*/
typedef struct {
OscTimeTag timestamp;
float gyroscopeX;
float gyroscopeY;
float gyroscopeZ;
float accelerometerX;
float accelerometerY;
float accelerometerZ;
float magnetometerX;
float magnetometerY;
float magnetometerZ;
float barometer;
} NgimuSensors;
/**
* @brief Timestamp and argument values for "/quaternion" message.
* Quaternion output of the on-board AHRS algorithm describing the
* orientation of the device relative to the Earth (NWU convention)
*/
typedef struct {
OscTimeTag timestamp;
float w;
float x;
float y;
float z;
} NgimuQuaternion;
/**
* @brief Timestamp and argument values for "/euler" message.
* Eluer angle output of the on-board AHRS as same as quaternion
*/
typedef struct {
OscTimeTag timestamp;
float roll;
float pitch;
float yaw;
} NgimuEuler;
/**
* @brief Timestamp and argument values for "/temperature" message.
* Temperature measurement data of the divice`s on-board temperature sensor
*/
typedef struct {
OscTimeTag timestamp;
float temp1;
float temp2;
} NgimuTemperature;
//------------------------------------------------------------------------------
// Function prototypes
void NgimuReceiveInitialise();
void NgimuReceiveSetReceiveErrorCallback(void (*newReceiveErrorCallback)(const char* const errorMessage));
void NgimuReceiveSetSensorsCallback(void (*newSensorsCallback)(const NgimuSensors ngimuSensors));
void NgimuReceiveSetQuaternionCallback(void (*newQuaternionCallback)(const NgimuQuaternion ngimuQuaternion));
void NgimuReceiveSetEulerCallback(void (*newEulerCallback)(const NgimuEuler ngimuEuler));
void NgimuReceiveSetTemperatureCallback(void (*newTemperatureCallback)(const NgimuTemperature ngimuEuler));
void NgimuReceiveProcessSerialByte(const char byte);
void NgimuReceiveProcessUdpPacket(const char * const source, const size_t sourceSize);
- NGIMU의 category별 data들을 struct 형식으로 묶어놓음
- /sensor
- /quaternion
- /euler
- /temperature
- 그리고 data receiving에 필요한 여러 함수들의 prototype들이 정의되어 있음
// NgimuReceive.cc
//------------------------------------------------------------------------------
// Variable declarations
static OscSlipDecoder oscSlipDecoder;
static void (*receiveErrorCallback)(const char* const errorMessage);
static void (*sensorsCallback)(const NgimuSensors ngimuSensors);
static void (*quaternionCallback)(const NgimuQuaternion ngimuQuaternion);
static void (*eulerCallback)(const NgimuEuler ngimuEuler);
static void (*temperatureCallback)(const NgimuTemperature ngimuTemperature);
- Variable 선언 부분
- 변수 분석
1) OscSlipDecoder : SLIP 관련 부분
// OscSlip.h
/**
* @brief OSC SLIP decoder buffer size. If a packet size exceeds the buffer
* size then all bytes in the decoder buffer will be discarded.
*/
#define OSC_SLIP_DECODER_BUFFER_SIZE (MAX_TRANSPORT_SIZE)
/**
* @brief OSC SLIP decoder structure. Structure members are used internally and
* should not be used by the user application.
*/
typedef struct {
char buffer[OSC_SLIP_DECODER_BUFFER_SIZE];
unsigned int bufferIndex;
void ( *processPacket)(OscPacket * const oscPacket);
} OscSlipDecoder;
- 내부적으로 사용되는 structure
- SLIP(Serial Line Internet Protocol) : TCP/IP가 Serial Communication을 통해 작동할 때 사용하는 Protocol
- MAX_TRANSPORT_SIZE는 OscCommon.h file에 정의되어 있다
// OscCommon.h
/**
* @brief Maximum packet size permitted by the transport layer. Reducing this
* value will reduce the amount of memory required.
*/
#define MAX_TRANSPORT_SIZE (1472)
- UDP의 최대 수신 용량이 1472byte
2) 나머지 variable들은 Callback function을 receive할 때 새로운 값을 저장하는데 쓰임
- 함수 분석
1) NgimuReceiveInitialise
/**
* @brief Initialises module. This function should be called once on system
* start up.
*/
void NgimuReceiveInitialise() {
OscSlipDecoderInitialise(&oscSlipDecoder);
oscSlipDecoder.processPacket = ProcessPacket;
}
2) ~~ Callback
- 다양한 값들에 대한 Callback function들을 receive함
// NgimuReceive.cc
/**
* @brief Sets receive error callback function.
* @param newReceiveErrorCallback Receive error callback function.
*/
void NgimuReceiveSetReceiveErrorCallback(void (*newReceiveErrorCallback)(const char* const errorMessage)) {
receiveErrorCallback = newReceiveErrorCallback;
}
/**
* @brief Sets receive "/sensors" callback function.
* @param newSensorsCallback "/sensors" callback function.
*/
void NgimuReceiveSetSensorsCallback(void (*newSensorsCallback)(const NgimuSensors ngimuSensors)) {
sensorsCallback = newSensorsCallback;
}
/**
* @brief Sets receive "/quaternion" callback function.
* @param newQuaternionCallback "/quaternion" callback function.
*/
void NgimuReceiveSetQuaternionCallback(void (*newQuaternionCallback)(const NgimuQuaternion ngimuQuaternion)) {
quaternionCallback = newQuaternionCallback;
}
/**
* @brief Sets receive "/euler" callback function.
* @param newEulerCallback "/euler" callback function.
*/
void NgimuReceiveSetEulerCallback(void (*newEulerCallback)(const NgimuEuler ngimuEuler)) {
eulerCallback = newEulerCallback;
}
/**
* @brief Sets receive "/temperature" callback function.
* @param newTemperatureCallback "/temperature" callback function.
*/
void NgimuReceiveSetTemperatureCallback(void (*newTemperatureCallback)(const NgimuTemperature ngimuEuler)) {
temperatureCallback = newTemperatureCallback;
}
3) NgimuReceiveProcessUdpPacket : Wifi Communication Related
// NgimuReceive.cc
/**
* @brief Process UDP packet received from NGIMU via Wi-Fi.
* @param source Address of source byte array.
* @param sourceSize Source size.
*/
void NgimuReceiveProcessUdpPacket(const char * const source, const size_t sourceSize) {
OscPacket oscPacket;
OscPacketInitialiseFromCharArray(&oscPacket, source, sourceSize);
oscPacket.processMessage = &ProcessMessage;
OscPacketProcessMessages(&oscPacket);
}
- OscPacket : OSC 통신의 Packet을 설정하는 structure
- OscPacket.h에 정의되어 있음
// OscPacket.h
/**
* @brief Maximum OSC packet size. The OSC packet size is limited by the
* maximum packet size permitted by the transport layer.
*/
#define MAX_OSC_PACKET_SIZE (MAX_TRANSPORT_SIZE)
/**
* @brief OSC packet structure. Structure members are used internally and
* should not be used by the user application.
*/
typedef struct {
char contents[MAX_OSC_PACKET_SIZE]; // 1472
size_t size; // Defined in stddef.h
void ( *processMessage)(const OscTimeTag * const oscTimeTag, OscMessage * const oscMessage);
} OscPacket;
- MAX_OSC_PACKET_SIZE는 MAX_TRANSPORT_SIZE와 마찬가지로 OscCommon.h file에 1472정의되어 있다
- OscMessgae는 OscMessage.h file에 정의되어 있다
// OScMessgae.h
/**
* @brief Maximum string length (excludes terminating null characters) of an OSC
* address pattern. This value may be modified as required by the user
* application.
*/
#define MAX_OSC_ADDRESS_PATTERN_LENGTH (64)
/**
* @brief Maximum length of an OSC type tag string (includes comma but not
* terminating null characters).
*/
#define MAX_OSC_TYPE_TAG_STRING_LENGTH (1 + MAX_NUMBER_OF_ARGUMENTS)
/**
* @brief Maximum combined size (number of bytes) of all arguments that may be
* contained within an OSC message. The calculation assumes the worst case of
* and extra 4 null characters for both the OSC address pattern and the OSC type
* tag string.
*/
#define MAX_ARGUMENTS_SIZE (MAX_OSC_MESSAGE_SIZE - (MAX_OSC_ADDRESS_PATTERN_LENGTH + 4) - (MAX_OSC_TYPE_TAG_STRING_LENGTH + 4))
/**
* @brief OSC message structure. Structure members are used internally and
* should not be used by the user application.
*/
typedef struct {
char oscAddressPattern[MAX_OSC_ADDRESS_PATTERN_LENGTH + 1]; // must be first member so that first byte of structure is equal to '/'. Null terminated.
char oscTypeTagString[MAX_OSC_TYPE_TAG_STRING_LENGTH + 1]; // includes comma. Null terminated
char arguments[MAX_ARGUMENTS_SIZE];
size_t oscAddressPatternLength; // does not include null characters
size_t oscTypeTagStringLength; // includes comma but not null characters
size_t argumentsSize;
unsigned int oscTypeTagStringIndex;
unsigned int argumentsIndex;
} OscMessage;
- OscMessage : OSC Message를 구성하는 Structure
- oscAddressPattern : 65bytes length. Structure의 첫번째 byte가 '/'와 같아져야 하므로 해당 변수가 반드시 첫번째 member이어야 함
- oscTypeTagString : 17bytes legnth. ',' 포함
- OscPacketInitialiseFromCharArray : OscPacket.c에 정의되어 있음
- 실제로 source로부터 받아온 data를 OSC Packet에 저장하는 부분
/**
* @brief Initialises an OSC packet from byte array.
*
* An OSC packet must be initialised before use. This function is used to
* initialise an OSC packet from a byte array and is typically of use when
* constructing an OSC packet from received bytes.
*
* Example use:
* @code
* OscPacket oscPacket;
* const char source[] = "/example\0\0\0\0,\0\0"; // string terminating null character is part of OSC message
* OscPacketInitialiseFromCharArray(&oscPacket, source, sizeof(source));
* @endcode
*
* @param oscPacket OSC packet to be initialised.
* @param source Byte array.
* @param numberOfBytes Number of bytes in byte array.
* @return Error code (0 if successful).
*/
OscError OscPacketInitialiseFromCharArray(OscPacket * const oscPacket, const char * const source, const size_t numberOfBytes) {
oscPacket->size = 0; // Initialize oscPacket`s size to zero
if (numberOfBytes > MAX_OSC_PACKET_SIZE) {
return OscErrorPacketSizeTooLarge; // error: size exceeds maximum packet size
}
while (oscPacket->size < numberOfBytes) {
oscPacket->contents[oscPacket->size] = source[oscPacket->size];
oscPacket->size++; // Put data by source to oscPacket`s contents sequenitally
}
oscPacket->processMessage = NULL; // Make OscPacket`s communication related values to NULL
return OscErrorNone; // 0
}
- OscError는 OscError.h file에 정의되어 있다
- OscError.h : 모든 Error에 대한 경우 정의
static void ProcessMessage(const OscTimeTag * const oscTimeTag, OscMessage * const oscMessage);
- OscTimeTag는 OscCommon.h file에 정의되어 있다
// OscCommon.h
/**
* @brief OSC time tag. Same representation used by NTP timestamps. 64-bit argument
*/
typedef union {
uint64_t value;
struct __attribute__((__packed__)) {
uint32_t fraction;
uint32_t seconds;
}
dwordStruct;
struct __attribute__((__packed__)) {
#ifdef LITTLE_ENDIAN_PLATFORM
char byte0; // LSB
char byte1;
char byte2;
char byte3;
char byte4;
char byte5;
char byte6;
char byte7; // MSB
#else
char byte7; // MSB
char byte6;
char byte5;
char byte4;
char byte3;
char byte2;
char byte1;
char byte0; // LSB
#endif
}
byteStruct;
} OscTimeTag;
-
OscPacketProcessMessages는 OscPacket.c에 정의되어 있다
// OscPacket.c
/**
* @brief Processes the OSC packet to provide each OSC message contained within
* the packet to the user application with the associated OSC time tag (if the
* message is contained within a bundle).
*
* A ProcessMessage function must be implemented within the application and
* assigned to the OSC packet structure after initialisation. The
* ProcessMessage function will be called for each OSC message found within the
* OSC packet.
*
* Example use:
* @code
* void ProcessMessage(const OscTimeTag * const oscTimeTag, OscMessage * const oscMessage) {
* }
*
* void Main() {
* OscPacket oscPacket;
* const char source[] = "/example\0\0\0\0,\0\0\0";
* OscPacketInitialiseFromCharArray(&oscPacket, source, sizeof(source) - 1);
* oscPacket.processMessage = ProcessPacket;
* OscPacketProcessMessages(&oscPacket);
* }
* @endcode
*
* @param oscPacket OSC packet to be processed.
* @return Error code (0 if successful).
*/
OscError OscPacketProcessMessages(OscPacket * const oscPacket) {
if (oscPacket->processMessage == NULL) {
return OscErrorCallbackFunctionUndefined; // error: user function undefined
}
return DeconstructContents(oscPacket, NULL, oscPacket->contents, oscPacket->size);
}
- Packet 내의 각 OSC message에 OSC Packet을 관련된 OSC time tag와 함께 user Application에 제공하기 위해 OSC Packet을 처리하는 부분 (Message가 bundle 내에 포함되어 있는 경우)
- ProcessMessage function은 initialization 후 application 내에서 실행되어야 하며 OSC Packet Structure에 할당되어야 한다
-> 실제 data 할당 부분 종료
4) ProcessPacket : Receive한 OSC packet내의 각 message를 위해 실행되는 Callback Function
5) ProcessAddress : OSC Address Pattern에 따라 OSC message를 처리
// NgimuReceive.cc
/**
* @brief Callback function executed for each message found within received OSC
* packet.
* @param oscTimeTag OSC time tag associated with message.
* @param oscMessage Address of OSC message.
*/
static void ProcessMessage(const OscTimeTag * const oscTimeTag, OscMessage * const oscMessage) {
const OscError oscError = ProcessAddress(oscTimeTag, oscMessage);
if ((oscError != OscErrorNone) && (receiveErrorCallback != NULL)) {
receiveErrorCallback(OscErrorGetMessage(oscError));
}
}
/**
* @brief Process OSC message according to OSC address pattern.
* @param oscTimeTag OSC time tag associated with message.
* @param oscMessage Address of OSC message.
* @return Error code (0 if successful).
*/
static OscError ProcessAddress(const OscTimeTag * const oscTimeTag, OscMessage * const oscMessage) {
// Process known message types
if (OscAddressMatch(oscMessage->oscAddressPattern, "/sensors")) {
return ProcessSensors(oscTimeTag, oscMessage);
}
if (OscAddressMatch(oscMessage->oscAddressPattern, "/quaternion")) {
return ProcessQuaternion(oscTimeTag, oscMessage);
}
if (OscAddressMatch(oscMessage->oscAddressPattern, "/euler")) {
return ProcessEuler(oscTimeTag, oscMessage);
}
if (OscAddressMatch(oscMessage->oscAddressPattern, "/temperature")) {
return ProcessTemperature(oscTimeTag, oscMessage);
}
// OSC address not recognised
if (receiveErrorCallback != NULL) {
static char string[256];
snprintf(string, sizeof (string), "OSC address pattern not recognised: %s", oscMessage->oscAddressPattern);
receiveErrorCallback(string);
}
return OscErrorNone;
- OscAddressMatch는 OscAddress.c file에 정의되어 있음
// OscAddress.c
/**
* @brief Matches an OSC address pattern with a target OSC address.
*
* Returns true if the OSC address pattern matches the target OSC address. The
* target OSC address cannot contain any special characters: '?', ' *', '[]', or
* '{}'.
*
* Example use:
* @code
* OscMessage oscMessage;
* OscMessageInitialise(&oscMessage, "/example/oscAddress/pattern");
* if(OscAddressMatch(oscMessage.oscAddressPattern, "/example/oscAddress/pattern") == true) {
* printf("Match!");
* }
* @endcode
*
* @param oscAddressPattern OSC address pattern.
* @param oscAddress Target OSC address.
* @return True if the OSC address pattern and target oscAddress match.
*/
bool OscAddressMatch(const char * oscAddressPattern, const char * const oscAddress) {
return MatchLiteral(oscAddressPattern, oscAddress, false);
}
- Receive받은 OSC Address를 target OSC address와 비교
- Address가 일치할 경우 'true'를 return
- MatchLiteral function은 동일한 file 내에 선언되어있음
// OscAddress.c
/**
* @brief Matches literal OSC address pattern with target OSC address.
*
* The OSC address pattern is initially assumed to be literal and not to contain
* any special characters: '?', ' *', '[]', or '{}'. If a special character is
* found then the result of MatchExpression is returned. Matching literal OSC
* address patterns is faster than matching OSC address patterns that contain
* special characters.
*
* This is an internal function and cannot be called by the user application.
*
* @param oscAddressPattern First character of OSC address pattern.
* @param oscAddress First character of target OSC address.
* @param isPartial Flag indicating if a partial match is acceptable.
* @return True if OSC address pattern and target OSC address match.
*/
static bool MatchLiteral(const char * oscAddressPattern, const char * oscAddress, const bool isPartial) {
while (*oscAddressPattern != '\0') {
if (*oscAddress == '\0') {
if (isPartial == true) {
return true;
} else {
return MatchExpression(&oscAddressPattern, &oscAddress, isPartial); // handle trailing 'zero character' expressions
}
return false; // fail: OSC address pattern too short
}
switch (*oscAddressPattern) {
case '?':
case '*':
case '[':
case '{':
return MatchExpression(&oscAddressPattern, &oscAddress, isPartial);
default:
if (*oscAddressPattern != *oscAddress) {
return false; // fail: character mismatch
}
break;
}
oscAddressPattern++;
oscAddress++;
}
if (*oscAddress != '\0') {
return false; // fail: OSC address pattern too long
}
return true;
}
6) ProcessSensors : /sensors message 처리 부분
// NgimuReceive.cc
/**
* @brief Process "/sensors" message.
* @param oscTimeTag OSC time tag associated with message.
* @param oscMessage Address of OSC message.
* @return Error code (0 if successful).
*/
static OscError ProcessSensors(const OscTimeTag * const oscTimeTag, OscMessage * const oscMessage) {
// Do nothing if no callback assigned
if (sensorsCallback == NULL) {
return OscErrorNone;
}
// Get timestamp
NgimuSensors ngimuSensors;
ngimuSensors.timestamp = *oscTimeTag; // Messgae의 timestamp를 assign
// Get gyroscope X axis
OscError oscError;
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.gyroscopeX);
if (oscError != OscErrorNone) {
return oscError;
}
// Get gyroscope Y axis
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.gyroscopeY);
if (oscError != OscErrorNone) {
return oscError;
}
// Get gyroscope Z axis
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.gyroscopeZ);
if (oscError != OscErrorNone) {
return oscError;
}
// Get accelerometer X axis
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.accelerometerX);
if (oscError != OscErrorNone) {
return oscError;
}
// Get accelerometer Y axis
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.accelerometerY);
if (oscError != OscErrorNone) {
return oscError;
}
// Get accelerometer Z axis
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.accelerometerZ);
if (oscError != OscErrorNone) {
return oscError;
}
// Get magnetometer X axis
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.magnetometerX);
if (oscError != OscErrorNone) {
return oscError;
}
// Get magnetometer Y axis
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.magnetometerY);
if (oscError != OscErrorNone) {
return oscError;
}
// Get magnetometer Z axis
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.magnetometerZ);
if (oscError != OscErrorNone) {
return oscError;
}
// Get barometer
oscError = OscMessageGetArgumentAsFloat32(oscMessage, &ngimuSensors.barometer);
if (oscError != OscErrorNone) {
return oscError;
}
// Callback
sensorsCallback(ngimuSensors);
return OscErrorNone;
}
참고 자료 :
https://github.com/ericlai0323/ros-ngimu
https://x-io.co.uk/downloads/NGIMU-User-Manual-v1.6.pdf
http://mwultong.blogspot.com/2006/08/qna-encoding-decoding.html
https://learn.newmedia.dog/courses/physical-computing/week-04/lesson-02/
https://change-words.tistory.com/entry/127001-%EC%82%AC%EC%9A%A9%EC%9D%B4%EC%9C%A0
https://wildeveloperetrain.tistory.com/182
https://roads22.wordpress.com/2012/02/26/open-sound-control-osc/
https://jtrimind.github.io/troubleshooting/filesystem/
https://www.ibm.com/docs/ko/aix/7.1?topic=communications-serial-line-internet-protocol
'Project > Jaguar 4X4 Human Tracking' 카테고리의 다른 글
[Jaguar 4x4 Human Tracking] 8/17 ~18 (0) | 2023.08.18 |
---|---|
[Jaguar 4x4 Human Tracking] 7/28 (0) | 2023.07.28 |
[Jaguar 4x4 Human Tracking] 3/22 (0) | 2023.03.22 |