navcom.c
来自「gpsd, a popular GPS daemon.」· C语言 代码 · 共 1,249 行 · 第 1/4 页
C
1,249 行
/* * Driver for Navcom receivers using propietary NCT messages, a binary protocol. * * Vendor website: http://www.navcomtech.com/ * Technical references: http://www.navcomtech.com/support/docs.cfm * * Tested with two SF-2040G models * * At this stage, this driver implements the following commands: * * 0x20: Data Request (tell the unit which responses you want) * 0x3f: LED Configuration (controls the front panel LEDs -- for testing) * 0x1c: Test Support Block (again, blinks the front panel lights) * * and it understands the following responses: * * 0x06: Acknowledgement (without error) * 0x15: Negative Acknowledge * 0x86: Channel Status * 0xae: Identification Block * 0xb0: Raw Meas. Data Block * 0xb1: PVT Block * 0xb5: Pseudorange Noise Statistics * 0xd3: LBM DSP Status Block * 0xef: Clock Drift and Offset * * FIXME - I'm not too sure of the way I have computed the vertical positional error * I have used FOM as a scaling factor for VDOP, thusly VRMS = FOM/HDOP*VDOP * * By Diego Berge. Contact via web form at http://www.nippur.net/survey/xuc/contact * (the form is in Catalan, but you'll figure it out) * */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <math.h>#include <ctype.h>#include <unistd.h>#include <time.h>#include <sys/types.h>#include <inttypes.h>#include <stdio.h>#include "gpsd_config.h"#include "gpsd.h"#if defined(NAVCOM_ENABLE) && defined(BINARY_ENABLE)#define LITTLE_ENDIAN_PROTOCOL#include "bits.h"/* Have data which is 24 bits long */#define getsl24(buf,off) (int32_t)(((u_int32_t)getub((buf), (off)+2)<<24 | (u_int32_t)getub((buf), (off)+1)<<16 | (u_int32_t)getub((buf), (off))<<8)>>8)#define getul24(buf,off) (u_int32_t)(((u_int32_t)getub((buf), (off)+2)<<24 | (u_int32_t)getub((buf), (off)+1)<<16 | (u_int32_t)getub((buf), (off))<<8)>>8)/* And just to be difficult, Navcom is little endian but the GPS data stream is big endian. Some messages contain raw GPS data */#define getsw_be(buf, off) (int16_t)((((u_int16_t)getub(buf, (off)) << 8) \ | (u_int16_t)getub(buf, (off)+1)))#define getuw_be(buf, off) (u_int16_t)((((u_int16_t)getub(buf, (off)) << 8) \ | (u_int16_t)getub(buf, (off)+1)))#define getsl_be(buf, off) (int32_t)((((u_int16_t)getuw_be(buf, (off)) << 16) \ | getuw_be(buf, (off)+2)))#define getul_be(buf, off) (u_int32_t)((((u_int16_t)getuw_be(buf, (off)) << 16) \ | getuw_be(buf, (off)+2)))#define getsL_be(buf, off) (int64_t)((((u_int64_t)getul_be(buf, (off)) << 32) \ | getul_be(buf, (off)+4)))#define getuL_be(buf, off) (u_int64_t)((((u_int64_t)getul_be(buf, (off)) << 32) \ | getul_be(buf, (off)+4)))#define getsl24_be(buf,off) (int32_t)(((u_int32_t)getub((buf), (off))<<24 \ | (u_int32_t)getub((buf), (off)+1)<<16 \ | (u_int32_t)getub((buf), (off)+2)<<8)>>8)#define NAVCOM_CHANNELS 12static u_int8_t checksum(unsigned char *buf, size_t len){ size_t n; u_int8_t csum = (u_int8_t)0x00; for(n = 0; n < len; n++) csum ^= buf[n]; return csum;}static bool navcom_send_cmd(struct gps_device_t *session, unsigned char *cmd, size_t len){ gpsd_report(LOG_RAW, "Navcom: command dump: %s\n", gpsd_hexdump(cmd, len)); return (gpsd_write(session, cmd, len) == (ssize_t)len);}/* Data Request */static void navcom_cmd_0x20(struct gps_device_t *session, u_int8_t block_id, u_int16_t rate){ unsigned char msg[18]; putbyte(msg, 0, 0x02); putbyte(msg, 1, 0x99); putbyte(msg, 2, 0x66); putbyte(msg, 3, 0x20); /* Cmd ID */ putword(msg, 4, 0x000e); /* Length */ putbyte(msg, 6, 0x00); /* Action */ putbyte(msg, 7, 0x01); /* Count of blocks */ putbyte(msg, 8, block_id); /* Data Block ID */ putbyte(msg, 9, 0x02); /* Logical Ports */ putword(msg, 10, rate); /* Data rate */ putbyte(msg, 12, 0x71); putbyte(msg, 13, 0x00); putword(msg, 14, 0x0000); putbyte(msg, 16, checksum(msg+3, 13)); putbyte(msg, 17, 0x03); (void)navcom_send_cmd(session, msg, 18); gpsd_report(LOG_PROG, "Navcom: sent command 0x20 (Data Request) " "- data block id = %02x at rate %02x\n", block_id, rate);}/*@ unused @*//* Changes the LED settings in the receiver */static void UNUSED navcom_cmd_0x3f(struct gps_device_t *session){ unsigned char msg[12]; putbyte(msg, 0, 0x02); putbyte(msg, 1, 0x99); putbyte(msg, 2, 0x66); putbyte(msg, 3, 0x3f); /* Cmd ID */ putword(msg, 4, 0x0008); putbyte(msg, 6, 0x01); /* Action */ putbyte(msg, 7, 0x00); /* Reserved */ putbyte(msg, 8, 0x02); /* Link LED setting */ putbyte(msg, 9, 0x0a); /* Battery LED setting */ putbyte(msg, 10, checksum(msg+3, 7)); putbyte(msg, 11, 0x03); (void)navcom_send_cmd(session, msg, 12); gpsd_report(LOG_PROG, "Navcom: sent command 0x3f (LED Configuration Block)\n");}/* Test Support Block - Blinks the LEDs */static void navcom_cmd_0x1c(struct gps_device_t *session, u_int8_t mode, u_int8_t length){ unsigned char msg[12]; putbyte(msg, 0, 0x02); putbyte(msg, 1, 0x99); putbyte(msg, 2, 0x66); putbyte(msg, 3, 0x1c); /* Cmd ID */ putword(msg, 4, 0x0008); putbyte(msg, 6, 0x04); /* Use ACK/NAK */ putbyte(msg, 7, mode); /* 0x01 or 0x02 */ putbyte(msg, 8, length); /* Only if mode == 0x01 */ putbyte(msg, 9, 0x00); putbyte(msg, 10, checksum(msg+3, 7)); putbyte(msg, 11, 0x03); (void)navcom_send_cmd(session, msg, 12); gpsd_report(LOG_PROG, "Navcom: sent command 0x1c (Test Support Block)\n"); gpsd_report(LOG_IO, "Navcom: command 0x1c mode = %02x, length = %u\n", mode, length);}/* Serial Port Configuration */static void navcom_cmd_0x11(struct gps_device_t *session, u_int8_t port_selection){ /* NOTE - We only allow changing one port at a time, although the message supports doing both at once. */ unsigned char msg[12]; putbyte(msg, 0, 0x02); putbyte(msg, 1, 0x99); putbyte(msg, 2, 0x66); putbyte(msg, 3, 0x11); /* Cmd ID */ putword(msg, 4, 0x0008); /* Length */ putbyte(msg, 6, 0x04); /* Action - Use ACK/NAK) */ putbyte(msg, 7, port_selection); putbyte(msg, 8, 0x00); /* Reserved */ putbyte(msg, 9, 0x00); /* Reserved */ putbyte(msg, 10, checksum(msg+3, 7)); putbyte(msg, 11, 0x03); (void)navcom_send_cmd(session, msg, 12); gpsd_report(LOG_PROG, "Navcom: sent command 0x11 (Serial Port Configuration)\n"); gpsd_report(LOG_IO, "Navcom: serial port selection: 0x%02x\n", port_selection);}static void navcom_probe_subtype(struct gps_device_t *session, unsigned int seq){ /* Request the following messages: */ if (seq==0) { /*@ +charint @*/ navcom_cmd_0x1c(session, 0x01, 5); /* Blink LEDs on receiver */ navcom_cmd_0x20(session, 0xae, 0x1770); /* Identification Block - send every 10 min*/ navcom_cmd_0x20(session, 0xb1, 0x4000); /* PVT Block */ navcom_cmd_0x20(session, 0xb5, 0x00c8); /* Pseudorange Noise Statistics - send every 20s */ navcom_cmd_0x20(session, 0xb0, 0x4000); /* Raw Meas Data Block */ navcom_cmd_0x20(session, 0x81, 0x0000); /* Packed Ephemeris Data - send once */ navcom_cmd_0x20(session, 0x81, 0x4000); /* Packed Ephemeris Data */ navcom_cmd_0x20(session, 0x86, 0x4000); /* Channel Status */ navcom_cmd_0x20(session, 0x83, 0x4000); /* Ionosphere and UTC Data */ navcom_cmd_0x20(session, 0xef, 0x0bb8); /* Clock Drift - send every 5 min */ /*@ -charint @*/ }}static void navcom_ping(struct gps_device_t *session){/* NOTE - This allows us to know into which of the unit's various serial ports we are connected. Its value gets updated every time we receive a 0x06 (Ack) message. Note that if commands are being fed into the unit from more than one port (which is entirely possible although not necessarily a bright idea), there is a good chance that we might misidentify our port */ /*@ -type @*/ session->driver.navcom.physical_port = 0xFF; navcom_cmd_0x1c(session, 0x02, 0); /* Test Support Block */ navcom_cmd_0x20(session, 0xae, 0x0000); /* Identification Block */ navcom_cmd_0x20(session, 0x86, 0x000a); /* Channel Status */ /*@ +type @*/}static bool navcom_speed(struct gps_device_t *session, unsigned int speed){#ifdef ALLOW_RECONFIGURE u_int8_t port_selection; u_int8_t baud; if (session->driver.navcom.physical_port == (unsigned char)0xFF) { /* We still don't know which port we're connected to */ return false; } /*@ +charint @*/ switch (speed) { /* NOTE - The spec says that certain baud combinations on ports A and B are not allowed, those are 1200/115200, 2400/57600, and 2400/115200. To try and minimise the possibility of those occurring, we do not allow baud rates below 4800. We could also disallow 57600 and 115200 to totally prevent this, but I do not consider that reasonable. Finding which baud speed the other port is set at would also be too much trouble, so we do not do it. */ case 4800: baud = 0x04; break; case 9600: baud = 0x06; break; case 19200: baud = 0x08; break; case 38400: baud = 0x0a; break; case 57600: baud = 0x0c; break; case 115200: baud = 0x0e; break; default: /* Unsupported speed */ return false; } /*@ -charint @*/ /* Proceed to construct our message */ port_selection = session->driver.navcom.physical_port | baud; /* Send it off */ navcom_cmd_0x11(session, port_selection); /* And cheekily return true, even though we have no way to know if the speed change succeeded until and if we receive an ACK (message 0x06), which will be at the new baud speed if the command was successful. Bottom line, the client should requery gpsd to see if the new speed is different than the old one */ return true;#else return false;#endif /* ALLOW_RECONFIGURE */}/* Ionosphere and UTC Data */static gps_mask_t handle_0x83(struct gps_device_t *session){ /* NOTE - At the present moment this is only being used for determining the GPS-UTC time difference, for which the iono data is not needed as far as we are concerned. However, I am still reporting it (if debuglevel >= LOG_IO) as a matter of interest *//* 2^-30 */#define SF_A0 (0.000000000931322574615478515625)/* 2^-50 */#define SF_A1 (0.000000000000000888178419700125)/* 2^12 */#define SF_TOT (4096)/* 2^-30 */#define SF_ALPHA0 (0.000000000931322574615478515625)/* 2^-27 */#define SF_ALPHA1 (0.000000007450580596923828125)/* 2^-24 */#define SF_ALPHA2 (0.000000059604644775390625)/* 2^-24 */#define SF_ALPHA3 (0.000000059604644775390625)/* 2^11 */#define SF_BETA0 (2048)/* 2^14 */#define SF_BETA1 (16384)/* 2^16 */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?