diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 2e3483a3..f1f42fb9 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -385,6 +385,7 @@ add_library( # Sets the name of the library. GPIOControl.cpp MicroPhoto.cpp PhoneDevice.cpp + PtzController.cpp # PhoneDevice2.cpp Camera.cpp Camera2Reader.cpp @@ -447,6 +448,7 @@ add_library( # Sets the name of the library. ${TERM_CORE_ROOT}/Client/Terminal_NX.cpp ${TERM_CORE_ROOT}/Client/Terminal_ZJ.cpp ${TERM_CORE_ROOT}/Client/Terminal_NW.cpp + ${TERM_CORE_ROOT}/Client/DataController.cpp ${TERM_CORE_ROOT}/Client/UpgradeReceiver.cpp ${TERM_CORE_ROOT}/Client/Database.cpp ${TERM_CORE_ROOT}/Client/SimulatorDevice.cpp diff --git a/app/src/main/cpp/PhoneDevice.cpp b/app/src/main/cpp/PhoneDevice.cpp index dbc56bca..39f230d1 100644 --- a/app/src/main/cpp/PhoneDevice.cpp +++ b/app/src/main/cpp/PhoneDevice.cpp @@ -565,6 +565,9 @@ CPhoneDevice::CPhoneDevice(JavaVM* vm, jobject service, const std::string& appPa GpioControl::setCam3V3Enable(true); GpioControl::setSpiPower(true); #endif + + m_ptzController = new PtzController(this); + m_ptzController->Startup(); } CPhoneDevice::~CPhoneDevice() @@ -1551,6 +1554,7 @@ bool CPhoneDevice::TakePhotoWithNetCamera(IDevice::PHOTO_INFO& localPhotoInfo, c // AutoEnv autoEnv(pThis->m_vm); time_t ts = time(NULL); uint32_t waitTime = localPhotoInfo.selfTestingTime; +#if 0 if(!GpioControl::GetSelftestStatus(waitTime)) { m_isSelfTesting.store(true); @@ -1558,6 +1562,7 @@ bool CPhoneDevice::TakePhotoWithNetCamera(IDevice::PHOTO_INFO& localPhotoInfo, c std::this_thread::sleep_for(std::chrono::milliseconds(waitTime)); m_isSelfTesting.store(false); } +#endif XYLOG(XYLOG_SEVERITY_DEBUG, "Ethernet Power ON"); @@ -2085,11 +2090,13 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector< } else if (photoInfo.cameraType == CAM_TYPE_PLZ) { +#if 0 XYLOG(XYLOG_SEVERITY_DEBUG, "PTZ PWR turned ON"); if(mPhotoInfo.scheduleTime == 0) powerCtrlPtr = std::shared_ptr(new PlzCameraPowerCtrl(mPhotoInfo.closeDelayTime)); else powerCtrlPtr = std::shared_ptr(new PlzCameraPowerCtrl(2)); +#endif } res = true; @@ -2266,6 +2273,8 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector< } else if (mPhotoInfo.mediaType == 0 && (mPhotoInfo.cameraType == CAM_TYPE_PLZ)) { + m_ptzController->AddPhotoCommand(mPhotoInfo, mPath, mOsds); +#if 0 uint64_t wid_serial = RequestWakelock(0); CPhoneDevice* pThis = this; IDevice::PHOTO_INFO localPhotoInfo = mPhotoInfo; @@ -2300,6 +2309,7 @@ bool CPhoneDevice::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector< }); t.detach(); +#endif } else if ((mPhotoInfo.mediaType == XY_MEDIA_TYPE_STREAM || mPhotoInfo.mediaType == XY_MEDIA_TYPE_STREAM_OFF) && (mPhotoInfo.cameraType == CAM_TYPE_NET || mPhotoInfo.cameraType == CAM_TYPE_PLZ)) { @@ -4366,6 +4376,9 @@ void CPhoneDevice::ConvertDngToPng(const std::string& dngPath, const std::string void CPhoneDevice::CameraCtrl(unsigned short waitTime, unsigned short delayTime, unsigned char channel, int cmdidx, unsigned char preset, const char *serfile, unsigned int baud, int addr) { + + m_ptzController->AddCommand(channel, cmdidx, 0, preset, serfile, baud, addr); +#if 0 if(GpioControl::GetSelftestStatus(waitTime) && GpioControl::GetCamerastatus()) { CPhoneDevice *pThis = this; string serfileStr(serfile); @@ -4382,6 +4395,7 @@ void CPhoneDevice::CameraCtrl(unsigned short waitTime, unsigned short delayTime, }); ctrlThread.detach(); } +#endif } int CPhoneDevice::GetSerialPhoto(int devno, D_IMAGE_DEF *photo) diff --git a/app/src/main/cpp/PhoneDevice.h b/app/src/main/cpp/PhoneDevice.h index 8d93d7fd..0e100af1 100644 --- a/app/src/main/cpp/PhoneDevice.h +++ b/app/src/main/cpp/PhoneDevice.h @@ -31,6 +31,8 @@ #include #include "SensorsProtocol.h" +#include "PtzController.h" + #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "error", __VA_ARGS__)) #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "debug", __VA_ARGS__)) @@ -168,6 +170,8 @@ class CPhoneDevice : public IDevice { public: + friend PtzController; + struct NETWORK { std::string iface; @@ -437,6 +441,8 @@ protected: std::map m_streamings; + PtzController* m_ptzController; + }; diff --git a/app/src/main/cpp/PtzController.cpp b/app/src/main/cpp/PtzController.cpp new file mode 100644 index 00000000..9a0a5c4d --- /dev/null +++ b/app/src/main/cpp/PtzController.cpp @@ -0,0 +1,384 @@ +// +// Created by Matthew on 2025/3/5. +// + +#include "PtzController.h" +#include "SensorsProtocol.h" +#include "GPIOControl.h" +#include "PhoneDevice.h" +#include "time.h" +#include + +PtzController::PtzController(CPhoneDevice* pPhoneDevice) : m_pPhoneDevice(pPhoneDevice) +{ + m_exit = false; +} + +void PtzController::Startup() +{ + m_thread = std::thread(PtzThreadProc, this); +} + +void PtzController::PtzThreadProc(PtzController* pThis) +{ + pThis->PtzProc(); +} + +void PtzController::AddCommand(uint8_t channel, int cmdidx, uint8_t bImageSize, uint8_t preset, const char *serfile, uint32_t baud, int addr) +{ + SERIAL_CMD cmd = { 0 }; + cmd.channel = channel; + cmd.preset = preset; + cmd.cmdidx = cmdidx; + cmd.bImageSize = bImageSize; + strcpy(cmd.serfile, serfile); + cmd.baud = baud; + cmd.addr = addr; + cmd.ts = time(NULL); + + m_locker.lock(); + m_cmds.push_back(cmd); + m_locker.unlock(); + + m_sem.release(); +} + +void PtzController::AddPhotoCommand(IDevice::PHOTO_INFO& photoInfo, const std::string& path, const std::vector& osds) +{ + IDevice::SerialsPhotoParam param = { "", 0, 0 }; + m_pPhoneDevice->GetPhotoSerialsParamCb(param); + + SERIAL_CMD cmdPreset = { 0 }; + time_t ts = time(NULL); + +#if 1 + // if (photoInfo.preset != 0 && photoInfo.preset != 0xFF) + { + cmdPreset.ts = photoInfo.selfTestingTime; + cmdPreset.delayTime = photoInfo.closeDelayTime; + cmdPreset.channel = photoInfo.channel; + cmdPreset.channel = photoInfo.preset; + cmdPreset.cmdidx = PHOTO_OPEN_POWER; + strcpy(cmdPreset.serfile, param.serfile); + cmdPreset.baud = param.baud; + cmdPreset.addr = param.addr; + } +#endif + SERIAL_CMD cmd = { 0 }; + cmd.ts = ts; + cmd.delayTime = photoInfo.closeDelayTime; + cmd.channel = photoInfo.channel; + cmd.preset = photoInfo.preset; + cmd.cmdidx = Take_Photo; + cmd.bImageSize = photoInfo.resolution; + + strcpy(cmd.serfile, param.serfile); + cmd.baud = param.baud; + cmd.addr = param.addr; + + PtzPhotoParams* ppp = new PtzPhotoParams(photoInfo, path, osds); + cmd.photoParams.reset(ppp); + // cmd.delayTime; + // uint8_t bImageSize; + // char serfile[128]; + // uint32_t baud; + // int addr; + m_locker.lock(); +#if 1 + if (cmdPreset.cmdidx != 0) + { + m_cmds.push_back(cmdPreset); + } +#endif + m_cmds.push_back(cmd); + m_locker.unlock(); + + m_sem.release(); + m_sem.release(); +} + +void PtzController::ExitAndWait() +{ + m_exit = true; + m_sem.release(); + + if (m_thread.joinable()) + { + m_thread.join(); + } +} + +void PtzController::PtzProc() +{ + PROC_PTZ_STATE state = PTZS_POWER_OFF; + SERIAL_CMD cmd; + PTZ_STATE ptz_state; + bool hasCmd = false; + int i=0; + char buffer[512]; + + std::shared_ptr powerCtrl; + time_t selfTestingStartTime = 0; + time_t selfTestingWaitTime = 0; + time_t PTZ_preset_start_time = 0; + time_t PTZ_preset_wait_time = 0; + time_t close_delay_time = CAMERA_CLOSE_DELAYTIME; + time_t start_delay_time = 0; + time_t photo_move_preset_time = 0; + + while(true) + { + m_sem.acquire(); + + if (m_exit) + { + break; + } + + hasCmd = false; + + m_locker.lock(); + for (auto it = m_cmds.begin(); it != m_cmds.end(); ++it) + { + if ((state == PTZS_SELF_TESTING) || (PTZS_PHOTO_SELF_TESTING == state)) + { + // find first non-taking-photo cmd + if (it->cmdidx != Take_Photo) + { + cmd = *it; + m_cmds.erase(it); + hasCmd = true; + break; + } + } + else + { + cmd = *it; + m_cmds.erase(it); + hasCmd = true; + break; + } + } + m_locker.unlock(); + + if (!hasCmd) + { + if ((state == PTZS_SELF_TESTING) || (PTZS_PHOTO_SELF_TESTING == state)) + { + time_t timeout = time(NULL) - selfTestingStartTime; + if(timeout < 0) + selfTestingStartTime = time(NULL); + if (timeout >= selfTestingWaitTime) + { + XYLOG(XYLOG_SEVERITY_INFO, "超时(%d秒)未收到云台自检结束应答,状态改为空闲!", timeout); + state = PTZS_IDLE; + m_sem.release(); + continue; + } + else + { + //if(timeout >= CAMERA_SELF_TEST_TIME) + { + XYLOG(XYLOG_SEVERITY_INFO, "开始查询云台自检状态!timeout=%d秒", timeout); + if(0 == QueryPtzState(&ptz_state, QUERY_PTZ_STATE, cmd.serfile, cmd.baud, cmd.addr)) + { + if(0 == ptz_state.ptz_status) + { + XYLOG(XYLOG_SEVERITY_INFO, "收到云台自检结束应答,状态改为空闲!timeout=%d秒", timeout); + state = PTZS_IDLE; + m_sem.release(); + continue; + } + } + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + m_sem.release(); + continue; + } + if(0 == start_delay_time) + { + ; + } + else + { + if(time(NULL) - start_delay_time < 0) + {/* 防止等待关机期间,其他线程发生对时,改变了系统时间,导致长时间不会关摄像机电源*/ + start_delay_time = time(NULL); + } + if(time(NULL) - start_delay_time >= close_delay_time) + { + XYLOG(XYLOG_SEVERITY_INFO, "摄像机空闲时间超过%d秒!关闭摄像机!", close_delay_time); + } + else + { + m_sem.release(); + continue; + } + } + if (state == PTZS_POWER_OFF) + { + XYLOG(XYLOG_SEVERITY_INFO, "自动关机触发,摄像机本来就处于关机状态!"); + // Do Nothing + } + else + { + XYLOG(XYLOG_SEVERITY_INFO, "自动关机触发,通知云台准备关机!state=%d", state); + for(i=0; i<3; i++) + { + if(0 == QueryPtzState(&ptz_state, NOTIFY_PTZ_CLOSE, cmd.serfile, cmd.baud, cmd.addr)) + break; + } + powerCtrl.reset(); + state = PTZS_POWER_OFF; + XYLOG(XYLOG_SEVERITY_INFO, "自动触发关闭云台电源!state=%d", state); + } + start_delay_time = 0; + continue; + } + + switch (cmd.cmdidx) + { + case Take_Photo: + { + if (state == PTZS_POWER_OFF) + { + if (!powerCtrl) + { + //powerCtrl = std::make_shared(cmd.photoParams->mPhotoInfo.closeDelayTime); + powerCtrl = std::make_shared(0); + selfTestingStartTime = time(NULL); + selfTestingWaitTime = cmd.photoParams->mPhotoInfo.selfTestingTime; + state = PTZS_PHOTO_SELF_TESTING; + XYLOG(XYLOG_SEVERITY_INFO, "1、收到拍照指令,摄像机从关机状态改为自检状态!"); + + m_locker.lock(); + m_cmds.insert(m_cmds.begin(), cmd); + m_locker.unlock(); + m_sem.release(); + continue; + } + } + if(cmd.photoParams->mPhotoInfo.scheduleTime == 0) + { + start_delay_time = time(NULL); + memset(buffer, 0, sizeof(buffer)); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localtime(&start_delay_time)); + XYLOG(XYLOG_SEVERITY_INFO, "3、收到手动拍照指令,state=%d!%s", state, buffer); + } + else + XYLOG(XYLOG_SEVERITY_INFO, "2、收到自动拍照指令,state=%d!", state); + + state = PTZS_TAKING_PHOTO; + if (cmd.preset != 0 && cmd.preset != 0xFF) + { + CameraPhotoCmd(0, cmd.channel, MOVE_PRESETNO, 0, cmd.preset, cmd.serfile, cmd.baud, cmd.addr); + + PTZ_preset_start_time = time(NULL); + PTZ_preset_wait_time = MOVE_PRESET_WAIT_TIME; + XYLOG(XYLOG_SEVERITY_INFO, "摄像机拍照前开始调用预置点%d!state=%d", cmd.preset, state); + for(;;) + { + if(0 == QueryPtzState(&ptz_state, QUERY_PTZ_STATE, cmd.serfile, cmd.baud, cmd.addr)) + { + if(0 == ptz_state.ptz_status) + { + XYLOG(XYLOG_SEVERITY_INFO, "摄像机拍照前调用预置点%d,收到移动结束应答!移动时长=%d秒 state=%d", cmd.preset, time(NULL)-PTZ_preset_start_time, state); + break; + } + } + if(time(NULL) - PTZ_preset_start_time >= PTZ_preset_wait_time) + { + XYLOG(XYLOG_SEVERITY_INFO, "摄像机拍照前调用预置点%d,摄像机在%d秒内未收到调用预置点结束应答!state=%d", cmd.preset, PTZ_preset_wait_time, state); + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + photo_move_preset_time = time(NULL); + } + } + m_pPhoneDevice->TakePhotoWithNetCamera(cmd.photoParams->mPhotoInfo, cmd.photoParams->mPath, cmd.photoParams->mOsds, powerCtrl); + state = PTZS_IDLE; + } + + break; + case PHOTO_OPEN_POWER: + if (state == PTZS_POWER_OFF) + { + if (!powerCtrl) + { + powerCtrl = std::make_shared(0); + selfTestingStartTime = time(NULL); + selfTestingWaitTime = 150; + state = PTZS_PHOTO_SELF_TESTING; + m_sem.release(); + XYLOG(XYLOG_SEVERITY_INFO, "收到拍照指令开机,摄像机从关机状态改为自检状态!设置的自检等待时间%d秒", selfTestingWaitTime); + } + } + else + { + XYLOG(XYLOG_SEVERITY_INFO, "收到拍照指令开机,摄像机处于state=%d!", state); + } + break; + case OPEN_TOTAL: + if (state == PTZS_POWER_OFF) + { + if (!powerCtrl) + { + powerCtrl = std::make_shared(0); + selfTestingStartTime = time(NULL); + selfTestingWaitTime = 150; + state = PTZS_SELF_TESTING; + m_sem.release(); + XYLOG(XYLOG_SEVERITY_INFO, "收到手动开机指令,摄像机从关机状态改为自检状态!设置的自检等待时间%d秒", selfTestingWaitTime); + } + } + else + { + XYLOG(XYLOG_SEVERITY_INFO, "收到手动开机指令,摄像机处于state=%d!", state); + } + start_delay_time = time(NULL); + memset(buffer, 0, sizeof(buffer)); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localtime(&start_delay_time)); + XYLOG(XYLOG_SEVERITY_INFO, "收到手动打开摄像机指令,刷新关机计时初始值,state=%d!%s", state, buffer); + break; + case CLOSE_TOTAL: + if (state == PTZS_POWER_OFF) + { + XYLOG(XYLOG_SEVERITY_INFO, "收到关机指令,摄像机本来就处于关机状态!"); + // Do Nothing + } + else if(PTZS_PHOTO_SELF_TESTING == state) + { + XYLOG(XYLOG_SEVERITY_INFO, "在拍照自检过程中收到关机指令,取消延时关机,转到自动关机处理!state=%d", state); + } + else + { + XYLOG(XYLOG_SEVERITY_INFO, "收到关机指令,通知云台准备关机!state=%d", state); + for(i=0; i<3; i++) + { + if(0 == QueryPtzState(&ptz_state, NOTIFY_PTZ_CLOSE, cmd.serfile, cmd.baud, cmd.addr)) + break; + } + powerCtrl.reset(); + state = PTZS_POWER_OFF; + XYLOG(XYLOG_SEVERITY_INFO, "关闭云台电源!state=%d", state); + } + start_delay_time = 0; + break; + default: + { + start_delay_time = time(NULL); + memset(buffer, 0, sizeof(buffer)); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localtime(&start_delay_time)); + XYLOG(XYLOG_SEVERITY_INFO, "收到手动控制摄像机指令,刷新关机计时初始值,state=%d!%s", state, buffer); + if(cmd.ts <= photo_move_preset_time) + { + XYLOG(XYLOG_SEVERITY_INFO, "丢弃拍照调预置点期间收到的控制云台指令,指令时间%ld, 拍照时间%ld!state=%d", cmd.ts, photo_move_preset_time, state); + } + else + CameraPhotoCmd(cmd.ts, cmd.channel, cmd.cmdidx, 0, cmd.preset, cmd.serfile, cmd.baud, cmd.addr); + } + break; + } + } +} \ No newline at end of file diff --git a/app/src/main/cpp/PtzController.h b/app/src/main/cpp/PtzController.h new file mode 100644 index 00000000..badd3ed6 --- /dev/null +++ b/app/src/main/cpp/PtzController.h @@ -0,0 +1,98 @@ +// +// Created by Matthew on 2025/3/5. +// + +#ifndef MICROPHOTO_PTZCONTROLLER_H +#define MICROPHOTO_PTZCONTROLLER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +enum PROC_PTZ_STATE +{ + PTZS_POWER_OFF = 0, + PTZS_IDLE = 1, + PTZS_SELF_TESTING = 2, + PTZS_MOVING = 3, + PTZS_TAKING_PHOTO = 4, + PTZS_PHOTO_SELF_TESTING = 5, +}; + +#define CAMERA_SELF_TEST_TIME 60 /* Camera self-test time (excluding PTZ self-test)*/ +#define MOVE_PRESET_WAIT_TIME 20 /* Waiting for the maximum time for the PTZ to move to the preset position*/ +#define CAMERA_CLOSE_DELAYTIME 120 /* Auto Power-Off Timer Setting After Manual Power-On (for Camera)*/ +#define PHOTO_OPEN_POWER 16000 +class PtzPhotoParams +{ +public: + PtzPhotoParams(const IDevice::PHOTO_INFO& photoInfo, const std::string& path, const std::vector& osds) : + mPhotoInfo(photoInfo), mPath(path), mOsds(osds) + { + } + + ~PtzPhotoParams() + { + } + + IDevice::PHOTO_INFO mPhotoInfo; + std::string mPath; + std::vector mOsds; +}; + +struct SERIAL_CMD +{ + uint8_t channel; + uint8_t preset; + time_t ts; + int cmdidx; + uint32_t delayTime; + uint8_t bImageSize; + char serfile[128]; + uint32_t baud; + int addr; + std::shared_ptr photoParams; +}; + + +class CPhoneDevice; +class PtzController +{ +public: + PtzController(CPhoneDevice* pPhoneDevice); + + void Startup(); + // (); + void AddCommand(uint8_t channel, int cmdidx, uint8_t bImageSize, uint8_t preset, const char *serfile, uint32_t baud, int addr); + void AddPhotoCommand(IDevice::PHOTO_INFO& photoInfo, const std::string& path, const std::vector& osds); + + void ExitAndWait(); + +protected: + static void PtzThreadProc(PtzController* pThis); + + void PtzProc(); + + +protected: + + +protected: + std::mutex m_locker; + std::vector m_cmds; + + CSemaphore m_sem; + bool m_exit; + + std::thread m_thread; + + CPhoneDevice* m_pPhoneDevice; +}; + + +#endif //MICROPHOTO_PTZCONTROLLER_H diff --git a/app/src/main/cpp/SensorsProtocol.cpp b/app/src/main/cpp/SensorsProtocol.cpp index d07e171b..99a5db3e 100644 --- a/app/src/main/cpp/SensorsProtocol.cpp +++ b/app/src/main/cpp/SensorsProtocol.cpp @@ -23,13 +23,14 @@ #include #include -#include +#include "AndroidHelper.h" #include "SensorsProtocol.h" //#include "Eint.h" #include pthread_mutex_t serial_mutex = PTHREAD_MUTEX_INITIALIZER; // 定义一个互斥锁 pthread_mutex_t camera_mutex = PTHREAD_MUTEX_INITIALIZER; // 定义一个互斥锁 +pthread_mutex_t bd_mutex = PTHREAD_MUTEX_INITIALIZER; // 定义一个互斥锁 SIO_PARAM_SERIAL_DEF serialport[MAX_SERIAL_PORT_NUM]; @@ -924,6 +925,7 @@ void Gm_FindAllSensorsCommand() case PELCO_P_PROTOCOL: /* 摄像机协议*/ case SERIALCAMERA_PROTOCOL: /* 串口摄像机协议*/ break; + default: break; } if (flag == -1) @@ -1485,7 +1487,7 @@ void CameraPhotoPortDataProcess(SIO_PARAM_SERIAL_DEF *curserial) case 1: // 自检 if (0 == curserial->ptz_state.ptz_status) sprintf(szbuf, "云台自检结束!"); - else if (1 == curserial->ptz_state.ptz_status) + else if (0 < curserial->ptz_state.ptz_status) sprintf(szbuf, "云台正在自检!"); else sprintf(szbuf, "云台自检发生错误!"); @@ -1494,9 +1496,9 @@ void CameraPhotoPortDataProcess(SIO_PARAM_SERIAL_DEF *curserial) case 2: // 调用预置点 if (0 == curserial->ptz_state.ptz_status) sprintf(szbuf, "调用预置位结束,云台处于所调预置位!"); - else if (1 == curserial->ptz_state.ptz_status) + else if (0 < curserial->ptz_state.ptz_status) sprintf(szbuf, "调用预置位,云台正在前往所调预置位位置!"); - else if (2 == curserial->ptz_state.ptz_status) + else if (2 == (curserial->ptz_state.ptz_status & 0x0f)) sprintf(szbuf, "调用预置位时,机芯电源未打开!"); else sprintf(szbuf, "调用预置位时,发生了错误,未正确执行!"); @@ -1505,7 +1507,7 @@ void CameraPhotoPortDataProcess(SIO_PARAM_SERIAL_DEF *curserial) case 3: // 一般状态 if (0 == curserial->ptz_state.ptz_status) sprintf(szbuf, "云台处于静止状态!"); - else if (1 == curserial->ptz_state.ptz_status) + else if (0 < curserial->ptz_state.ptz_status) sprintf(szbuf, "云台正在运动!"); else sprintf(szbuf, "云台发生错误!"); @@ -2415,13 +2417,23 @@ void MakeShxyProtocolPollCommand(int portno, uint8_t cmdidx) unsigned char CalLpc(unsigned char *msg, int len) { int i; - u_char retval = 0; + unsigned char retval = 0; for (i = 0; i < len; i++) retval += msg[i]; return retval; } +unsigned char BDXorCheck(unsigned char *msg, int len) +{ + int i; + unsigned char retval = 0; + + for (i = 0; i < len; i++) + retval ^= msg[i]; + return retval; +} + /*************************************************************** * 读上海欣影传感器协议数据 * ***************************************************************/ @@ -3264,14 +3276,17 @@ void GM_CameraSerialComRecv(SIO_PARAM_SERIAL_DEF *pPortParam) recvlen = read(pPortParam->fd, &recvbuf[i], sizeof(recvbuf)-i);/* 在串口读取字符串 */ t1 = get_msec(); if(t1-t0 >= 350) - break; + { + i += recvlen; + break; + } } recvlen = i; if (recvlen < 1) return; #if 1 - sprintf(buf, "收到Camera, %d字节数据:", recvlen); + sprintf(buf, "收到BD, %d字节数据:", recvlen); BytestreamLOG(0, buf, recvbuf, recvlen, 'I'); #endif @@ -3463,6 +3478,465 @@ int QueryPtzState(PTZ_STATE *ptz_state, int cmdidx, const char *serfile, unsign pthread_mutex_unlock(&camera_mutex); // 解锁 return flag; } + +int Query_BDGNSS_Data(BD_GNSS_DATA *BD_data, int samptime, const char *serfile, unsigned int baud) +{ + pthread_mutex_lock(&bd_mutex); + int flag = 0; + char szbuf[128], logbuf[128]; + SIO_PARAM_SERIAL_DEF *cameraport=NULL; + + if(NULL == BD_data) + return -1; + if(samptime < 5) + samptime = 5; + cameraport = (SIO_PARAM_SERIAL_DEF*)malloc(sizeof(SIO_PARAM_SERIAL_DEF)); + + cameraport->Retry = 0; + cameraport->RetryTime = 1000; + cameraport->WaitTime = 0; + cameraport->m_iRevStatus = 0; + cameraport->m_iRecvLen = 0; + cameraport->m_iNeedRevLength = 0; + cameraport->fd = -1; + memset(cameraport->m_au8RecvBuf, 0, RECVDATA_MAXLENTH); // 接收数据缓存区 + ClearCameraCmdFormPollCmdBuf(cameraport); + + flag = Gm_OpenCameraSerial(cameraport, serfile, baud); + +#if 1 + memset(szbuf, 0, sizeof(szbuf)); + srdt.iLastGetPhotoNo = -1; + cameraport->SerialCmdidx = -1; + cameraport->FirstCmdTimeCnt = get_msec(); +#endif + if(-1 == flag) + { + if(NULL != cameraport) + { + free(cameraport); + cameraport = NULL; + } + return -1; + } + + if (0x00 == flag) + { + sprintf(szbuf, "北斗定位数据查询启动串口定时器!"); + DebugLog(8, szbuf, 'I'); + for (;;) + { + usleep(10); + //LOGW("polltime=%ldms", get_msec()-polltime); + //polltime = get_msec(); + flag = GM_BdSerialTimer(cameraport); + if(get_msec() - cameraport->FirstCmdTimeCnt > samptime*1000) + { + DebugLog(8, "退出查询北斗定位数据流程!", 'V'); + memmove((void*)BD_data, &cameraport->bd_data, sizeof(BD_GNSS_DATA)); + break; + } + } + } + if(NULL != cameraport) + { + free(cameraport); + cameraport = NULL; + } + pthread_mutex_unlock(&bd_mutex); // 解锁 + return flag; +} + +int GM_BdSerialTimer(SIO_PARAM_SERIAL_DEF *pPortParam) +{ + int flag = 0; + + GM_BdSerialComRecv(pPortParam); + return flag; +} + +void GM_BdSerialComRecv(SIO_PARAM_SERIAL_DEF *pPortParam) +{ + int i, recvlen; + u_char recvbuf[RECVDATA_MAXLENTH]; + char buf[RECVDATA_MAXLENTH+256]; + int64_t t0, t1; + + t0 = get_msec(); + + memset(recvbuf, 0, sizeof(recvbuf)); + if (pPortParam->fd <= 0) + return; + i=0; + recvlen = 0; + memset(recvbuf, 0, sizeof(recvbuf)); + for(;;) + { + i += recvlen; + recvlen = read(pPortParam->fd, &recvbuf[i], sizeof(recvbuf)-i);/* 在串口读取字符串 */ + t1 = get_msec(); + if(t1-t0 >= 350) + break; + } + + recvlen = i; + if (recvlen < 1) + return; +#if 1 + sprintf(buf, "收到BD, %d字节数据:%s", recvlen, recvbuf); + DebugLog(0, (char*)recvbuf, 'I'); +#endif + + BdRecvData(pPortParam, recvbuf, recvlen); +} + +void BdRecvData(SIO_PARAM_SERIAL_DEF *pPortParam, u_char *buf, int len) +{ + int i; + unsigned char bdxor=0, srcxor=0; + + for (i = 0; i < len; i++) + { + switch (pPortParam->m_iRevStatus) + { + case 0: // 搜索起始符 '$' + pPortParam->m_iRecvLen = 0; + memset(pPortParam->m_au8RecvBuf, 0,sizeof(pPortParam->m_au8RecvBuf)); + if ('$' == buf[i]) + { + pPortParam->m_au8RecvBuf[pPortParam->m_iRecvLen++] = buf[i]; + pPortParam->m_iRevStatus = 1; // 进入数据接收状态 + } + break; + case 1: // 接收数据直到 '*' + if (pPortParam->m_iRecvLen >= RECVDATA_MAXLENTH) + { + // 缓冲区溢出,重置状态 + pPortParam->m_iRevStatus = 0; + pPortParam->m_iRecvLen = 0; + break; + } + pPortParam->m_au8RecvBuf[pPortParam->m_iRecvLen++] = buf[i]; + + if (buf[i] == '*') + { + pPortParam->m_iRevStatus = 2; // 进入校验码接收状态 + } + break; + case 2: // 接收校验码(2字节十六进制)和 + if (pPortParam->m_iRecvLen >= RECVDATA_MAXLENTH) + { + // 缓冲区溢出,重置状态 + pPortParam->m_iRevStatus = 0; + pPortParam->m_iRecvLen = 0; + break; + } + pPortParam->m_au8RecvBuf[pPortParam->m_iRecvLen++] = buf[i]; + + // 检测到换行符(0x0A),检查前一个字符是否为回车符(0x0D) + if (buf[i] == 0x0A) + { + if (pPortParam->m_iRecvLen >= 5 && + pPortParam->m_au8RecvBuf[pPortParam->m_iRecvLen - 2] == 0x0D) + { + // 提取校验码(*后的两个字符) + char hex_str[3] = { + (char)pPortParam->m_au8RecvBuf[pPortParam->m_iRecvLen - 4], + (char)pPortParam->m_au8RecvBuf[pPortParam->m_iRecvLen - 3], + '\0' + }; + srcxor = (uint8_t)strtol(hex_str, NULL, 16); + + // 计算校验值(从$后的第一个字符到*前的字符) + uint8_t calc_xor = BDXorCheck(&pPortParam->m_au8RecvBuf[1],pPortParam->m_iRecvLen - 6);// 长度 = 总长度 - ($ + *HH + \r\n) + + if (srcxor == calc_xor) + { + BD_NMEA0183_PortDataProcess(pPortParam); + pPortParam->RevCmdFlag = 1; + } + // 重置状态,准备接收下一帧 + pPortParam->m_iRevStatus = 0; + pPortParam->m_iRecvLen = 0; + } + else + { + // 格式错误,丢弃数据 + pPortParam->m_iRevStatus = 0; + pPortParam->m_iRecvLen = 0; + } + } + break; + case 255:// 错误接收数据 + default: + if (buf[i] == '$') + { + pPortParam->m_iRevStatus = 1; + pPortParam->m_iRecvLen = 1; + pPortParam->m_au8RecvBuf[0] = buf[i]; + } + else + { + pPortParam->m_au8RecvBuf[pPortParam->m_iRecvLen++] = buf[i]; + if (pPortParam->m_iRecvLen > 200) + { + pPortParam->m_iRecvLen = 0; + } + } + break; + } + } +} + +/* +$BDRMC,023656.00,A,2240.61563,N,11359.86512,E,0.16,,140324,,,A,V*2C +$BDVTG,,,,,0.16,N,0.30,K,A*2F +$BDGGA,023656.00,2240.61563,N,11359.86512,E,1,23,0.7,96.53,M,-3.52,M,,*5B +$BDGSA,A,3,01,02,03,04,05,06,07,09,10,16,19,20,1.0,0.7,0.8,4*30 +$BDGSV,6,1,23,01,45,125,38,02,46,235,40,03,61,189,46,04,32,112,37,1*7B +$BDGLL,2240.61563,N,11359.86512,E,023656.00,A,A*78 +$BDZDA,023656.00,14,03,2024,00,00*71 +$GPTXT,01,01,01,ANTENNA OPEN*25 + GLL:经度、纬度、UTC 时间 +GSA:北斗接收机操作模式,定位使用的卫星,DOP 值 +GSV:可见北斗卫星信息、仰角、方位角、信噪比(SNR) +RMC:时间、日期、位置、速度 +VTG:地面速度信息 +ZDA: 时间、日期 +TXT:用于天线状态检测 + */ +void BD_NMEA0183_PortDataProcess(SIO_PARAM_SERIAL_DEF *curserial) +{ + BD_NMEA0183_PROC_FUNC bd_nmea0183_call[] = { + {"$BDRMC",BD_get_BDRMC_data},/* 时间、日期、位置、速度*/ + {"$BDVTG",NULL},/* 地面速度信息*/ + {"$BDGGA",NULL},/* 时间、位置、定位类型*/ + {"$BDGSA",NULL},/* 北斗接收机操作模式,定位使用的卫星,DOP 值 */ + {"$BDGSV",NULL},/* 可见北斗卫星信息、仰角、方位角、信噪比(SNR)*/ + {"$BDGLL",NULL},/* 经度、纬度、UTC 时间*/ + {"$BDZDA",NULL},/* 时间、日期 */ + {"$GPTXT",NULL},/* 用于天线状态检测*/ + };/* irows*/ + int i=0, irows; + char szbuf[512]; + //char *cmd=NULL, *sourestr=NULL; + //RTUMSG rtumsg; + + if(NULL == curserial) + return; + irows = sizeof(bd_nmea0183_call)/sizeof(BD_NMEA0183_PROC_FUNC); + memset(szbuf, 0, sizeof(szbuf)); + //sprintf(szbuf, "rows = %d", irows); + DebugLog(0, (char*)curserial->m_au8RecvBuf, 'I'); + +#if 0 + memset((void*)rtumsg.MsgData, 0, sizeof(rtumsg.MsgData)); + memcpy((void*)rtumsg.MsgData, (void*)curserial->m_au8RecvBuf, curserial->m_iRecvLen); + rtumsg.MsgLen = curserial->m_iRecvLen; + /* 析出关键字符串*/ + cmd = (char *)rtumsg.MsgData; + sourestr = strstr((char *)rtumsg.MsgData, ","); + *sourestr = 0; + sprintf(szbuf, "cmd_len = %d, cmd:%s", strlen(cmd), cmd); + DebugLog(0, szbuf, 'I'); +#endif + + for(i=0; im_au8RecvBuf, bd_nmea0183_call[i].cmd_name)) + { + sprintf(szbuf, "cmd_name[%d]:%s", i, bd_nmea0183_call[i].cmd_name); + DebugLog(0, szbuf, 'I'); + if(NULL != bd_nmea0183_call[i].recv_process) + bd_nmea0183_call[i].recv_process(curserial); + break; + } + } + if(i >= irows) + return; +} + +int BD_get_BDRMC_data(SIO_PARAM_SERIAL_DEF *curserial) +{ + const int UTC_TIME = 1; + const int STATUS = 2; + const int LAT = 3; + const int ULAT = 4; + const int LON = 5; + const int ULON = 6; + const int DATE = 9; + double dvalue; + int total_fields=0, ivalue; + char **result = NULL; + char buffer[128]; // 存储格式化时间的缓冲区 + + if(NULL == curserial) + return -1; + /* + 1 $--RMC 字符串 消息 ID,RMC 语句头,’--‘为系统标识 + 2 UTCtime hhmmss.ss 当前定位的 UTC 时间 + 3 status 字符串 位置有效标志。 V=接收机警告,数据无效 A=数据有效 + 4 lat ddmm.mmmmm 纬度,前 2 字符表示度,后面的字符表示分 + 5 uLat 字符 纬度方向:N-北,S-南 + 6 lon dddmm.mmmmm 经度,前 3 字符表示度,后面的字符表示分 + 7 uLon 字符 经度方向:E-东,W-西 + 8 spd 数值 对地速度,单位为节 + 9 cog 数值 对地真航向,单位为度 + 10 date ddmmyy 日期(dd 为日,mm 为月,yy 为年) + 11 mv 数值 磁偏角,单位为度。固定为空 + 12 mvE 字符 磁偏角方向:E-东,W-西。固定为空 + 13 mode 字符 定位模式标志(备注[1]) + 14 navStatus 字符 导航状态标示符(V 表示系统不输出导航状态信息) 仅 NMEA 4.1 及以上版本有效 + 15 CS 16 进制数值 校验和,$和*之间(不包括$和*)所有字符的异或结果 + 16 字符 回车与换行符*/ + + result = BD_NMEA0183_SplitString((char *)curserial->m_au8RecvBuf, &total_fields); + if(NULL == result) + return -1; + + dvalue = ::atof(result[UTC_TIME]); + curserial->bd_data.UTC_time.tm_sec = (int)dvalue%100; + curserial->bd_data.UTC_time.tm_min = (int)dvalue/100%100; + curserial->bd_data.UTC_time.tm_hour = (int)dvalue/10000; + curserial->bd_data.ms_time = (dvalue - ((int)dvalue/1))*1000; + + curserial->bd_data.status = result[STATUS][0]; + dvalue = ::atof(result[LAT]); + curserial->bd_data.lat = ((int)dvalue/100)+(dvalue - ((int)dvalue/100*100))/60; + curserial->bd_data.uLat = result[ULAT][0]; + dvalue = ::atof(result[LON]); + curserial->bd_data.lon = ((int)dvalue/100)+(dvalue - ((int)dvalue/100*100))/60; + curserial->bd_data.uLon = result[ULON][0]; + + ivalue = ::atoi(result[DATE]); + ALOGD("%d", ivalue); + curserial->bd_data.UTC_time.tm_mday = ivalue/10000; + ALOGD("D:%d", curserial->bd_data.UTC_time.tm_mday); + curserial->bd_data.UTC_time.tm_mon = ivalue/100%100-1; + ALOGD("M:%d", curserial->bd_data.UTC_time.tm_mon); + curserial->bd_data.UTC_time.tm_year = ivalue%100+100; + ALOGD("Y:%d", curserial->bd_data.UTC_time.tm_year); + + ::memset(buffer, 0, sizeof(buffer)); + // 使用 strftime 格式化时间 + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &curserial->bd_data.UTC_time); + DebugLog(0, (char*)buffer, 'I'); + + ::sprintf(buffer, "ms:%d lat=%f ulat=%c", curserial->bd_data.ms_time, curserial->bd_data.lat, curserial->bd_data.uLat); + DebugLog(0, (char*)buffer, 'I'); + ::sprintf(buffer, "lon=%f ulon=%c, status=%c", curserial->bd_data.lon, curserial->bd_data.uLon, curserial->bd_data.status); + DebugLog(0, (char*)buffer, 'I'); + + // 释放内存 + for (int i = 0; i < total_fields; ++i) free(result[i]); + free(result); + + return 0; +} + +char** BD_NMEA0183_SplitString(char *str, int *total_fields) +{ + int max_fields, field_count = 0; + char **fields = NULL, **new_fields = NULL, **result = NULL; + char *copy = NULL, *p = NULL; + char szbuf[128]; + + if(NULL == str) + return NULL; + if(NULL == total_fields) + return NULL; + + // 创建可修改的副本 + copy = strdup(str); + memset(szbuf, 0, sizeof(szbuf)); + if (!copy) + { + sprintf(szbuf, "内存分配失败\n"); + DebugLog(0, szbuf, 'E'); + return NULL; + } + + // 初始字段数组大小 + max_fields = MAX_FIELDS_NUM; + fields = (char**)malloc(max_fields * sizeof(char *)); + if (!fields) { + free(copy); + sprintf(szbuf, "fields 内存分配失败\n"); + DebugLog(0, szbuf, 'E'); + return NULL; + } + + field_count = 0; + fields[field_count] = copy; // 第一个字段起始 + + // 遍历字符串分割字段 + for (p = copy; *p; ++p) + { + if (*p == ',') + { + *p = '\0'; // 结束当前字段 + field_count++; + + // 动态扩展数组 + if (field_count >= max_fields) { + max_fields *= 2; + new_fields = (char**)realloc(fields, max_fields * sizeof(char *)); + if (!new_fields) { + free(fields); + free(copy); + sprintf(szbuf, "new_fields 内存分配失败\n"); + DebugLog(0, szbuf, 'E'); + return NULL; + } + fields = new_fields; + } + fields[field_count] = p + 1; // 下一字段起始 + } + } + + *total_fields = field_count + 1; // 总字段数 + + // 复制字段到独立内存 + result = (char **)malloc((*total_fields) * sizeof(char *)); + if (!result) + { + free(fields); + free(copy); + sprintf(szbuf, "result 内存分配失败\n"); + DebugLog(0, szbuf, 'E'); + return NULL; + } + + for (int i = 0; i < *total_fields; ++i) { + result[i] = strdup(fields[i]); + if (!result[i]) { + // 释放已分配内存 + for (int j = 0; j < i; ++j) free(result[j]); + free(result); + free(fields); + free(copy); + sprintf(szbuf, "result 字段复制失败\n"); + DebugLog(0, szbuf, 'E'); + return NULL; + } + } + + // 输出结果到日志 + for (int i = 0; i < *total_fields; ++i) + { + sprintf(szbuf, "字段 %2d: %s\n", i + 1, result[i]); + DebugLog(0, szbuf, 'I'); + } + + // 释放内存 + //for (int i = 0; i < total_fields; ++i) free(result[i]); + //free(result); + free(fields); + free(copy); + + return result; +} + /* 串口启动接口函数 结束*/ /* 数据和图片采集数据返回函数 开始*/ diff --git a/app/src/main/cpp/SensorsProtocol.h b/app/src/main/cpp/SensorsProtocol.h index 42e388fa..e2011891 100644 --- a/app/src/main/cpp/SensorsProtocol.h +++ b/app/src/main/cpp/SensorsProtocol.h @@ -27,6 +27,7 @@ #define IOT_PARAM_WRITE 0xAE #define IOT_PARAM_READ 0xAF +#define MAX_FIELDS_NUM 20 /* BD_NMEA0183单组字符串数据内含数据最大数量*/ #define MAX_SERIAL_DEV_NUM 25 /* 最大接串口传感器数量*/ #define MAX_SERIAL_PORT_NUM 5 #define MAX_DEV_VALUE_NUM 12 /* 一台装置最大的采样值数量*/ @@ -39,6 +40,7 @@ #define PELCO_D_PROTOCOL 6 /* 摄像机Pelco_D协议序号*/ #define SERIALCAMERA_PROTOCOL 8 /* 串口摄像机协议序号*/ #define MUTIWEATHER_PROTOCOL 9 /*多合一气象*/ +#define NMEA0183_PROTOCOL 10 /* 单一北斗NMEA0183标准协议*/ #define RESERVE2_PROTOCOL 17 /* 备用2协议序号*/ #define RESERVE4_PROTOCOL 19 /* 备用4协议序号*/ #define RESERVE5_PROTOCOL 20 /* 备用5协议序号*/ @@ -126,6 +128,7 @@ #define OPEN_MODULE_POWER 10020 /* 打开机芯电源(1 有效)*/ #define NOTIFY_PTZ_CLOSE 10021 // 通知云台关闭 #define QUERY_PTZ_STATE 10022 // 查询云台状态 +#define CLOSE_TOTAL 10040 /* 关闭总电源*/ #define SPEED_DOME_CAMERA 0 /* 球机摄像机*/ #define SERIAL_CAMERA 2 /* 串口摄像机a*/ @@ -225,6 +228,38 @@ typedef struct float x_coordinate; /* 云台所处位置水平方向坐标*/ float y_coordinate; /* 云台所处位置垂直方向坐标*/ } PTZ_STATE; +/* +$--RMC 字符串 消息 ID,RMC 语句头,’--‘为系统标识 +2 UTCtime hhmmss.ss 当前定位的 UTC 时间 +3 status 字符串 位置有效标志。 +V=接收机警告,数据无效 + A=数据有效 +4 lat ddmm.mmmmm 纬度,前 2 字符表示度,后面的字符表示分 +5 uLat 字符 纬度方向:N-北,S-南 +6 lon dddmm.mmmm + m +经度,前 3 字符表示度,后面的字符表示分 +7 uLon 字符 经度方向:E-东,W-西 +8 spd 数值 对地速度,单位为节 +9 cog 数值 对地真航向,单位为度 +10 date ddmmyy 日期(dd 为日,mm 为月,yy 为年) +11 mv 数值 磁偏角,单位为度。固定为空 +12 mvE 字符 磁偏角方向:E-东,W-西。固定为空 +13 mode 字符 定位模式标志(备注[1]) +14 navStatus 字符 导航状态标示符(V 表示系统不输出导航状态信息) +仅 NMEA 4.1 及以上版本有效 +15 CS 16 进制数值 校验和,$和*之间(不包括$和*)所有字符的异或结果*/ +// 北斗卫星数据 +typedef struct +{ + struct tm UTC_time; /* UTC时间*/ + int ms_time; /* 毫秒*/ + double lat; /* 纬度,原值(前 2 字符表示度,后面的字符表示分)转换后为° */ + char uLat; /* 纬度方向:N-北,S-南*/ + double lon; /* 经度,原值(前 3 字符表示度,后面的字符表示分)转换后为°*/ + char uLon; /* 经度'E'-东,'W'-西*/ + char status; /* 'A'=数据有效 其他字符表示数据无效*/ +} BD_GNSS_DATA; typedef struct { @@ -247,18 +282,27 @@ typedef struct uint16_t ForceWaitCnt; /* 强制等待计数*/ uint8_t ReSendCmdFlag; /* 重发命令标志 */ uint8_t SendCmdFlag; /* 命令发送标志 */ - uint8_t RevCmdFlag; /* 命令正常接收标志*/ + uint8_t RevCmdFlag; /* 命令正常接收标志*/ //********************************************************** - int64_t lsendtime; /* 命令发送绝对时间计时(毫秒)*/ - int cameraaddr; /* 摄像机地址*/ + int64_t lsendtime; /* 命令发送绝对时间计时(毫秒)*/ + int cameraaddr; /* 摄像机地址*/ int SerialCmdidx; /* 正在使用的串口发送命令的命令序号(-1:表示没有命令发送) 摄像机使用命令序号存储*/ - PHOTO_DEF image; /* 临时存储图片数据*/ + PHOTO_DEF image; /* 临时存储图片数据*/ int64_t FirstCmdTimeCnt; /* 串口读取数据起始时间*/ PTZ_STATE ptz_state; - int sendptzstatecmd; + int sendptzstatecmd; // 查询命令次数控制 + BD_GNSS_DATA bd_data; + } SIO_PARAM_SERIAL_DEF; +typedef const struct +{ + //char *account; // 命令说明 + char *cmd_name; // 命令名称 + int (*recv_process)(SIO_PARAM_SERIAL_DEF *); /* urc数据处理*/ +}BD_NMEA0183_PROC_FUNC; + //串口相关装置所有参数集中定义 typedef struct { @@ -487,6 +531,23 @@ int GM_IsCloseCamera(SIO_PARAM_SERIAL_DEF *pPortParam); int GM_CameraSerialTimer(SIO_PARAM_SERIAL_DEF *pPortParam); int QueryPtzState(PTZ_STATE *ptz_state, int cmdidx, const char *serfile, unsigned int baud, int addr); + +int Query_BDGNSS_Data(BD_GNSS_DATA *BD_data, int samptime, const char *serfile, unsigned int baud); + +int GM_BdSerialTimer(SIO_PARAM_SERIAL_DEF *pPortParam); + +void GM_BdSerialComRecv(SIO_PARAM_SERIAL_DEF *pPortParam); + +void BdRecvData(SIO_PARAM_SERIAL_DEF *pPortParam, u_char *buf, int len); + +unsigned char BDXorCheck(unsigned char *msg, int len); + +void BD_NMEA0183_PortDataProcess(SIO_PARAM_SERIAL_DEF *curserial); + +char** BD_NMEA0183_SplitString(char *str, int *total_fields); + +int BD_get_BDRMC_data(SIO_PARAM_SERIAL_DEF *curserial); + #endif // __SENSOR_PROTOCOL_H__