Skip to content

Commit

Permalink
v5.0.0
Browse files Browse the repository at this point in the history
* HAL: Changed GPS module to get native GPS time (monotonic, no leap second).
**WARNING**: The native GPS time is not given in standard NMEA messages, so we
get it from a proprietary message of the GPS module used on gateway reference
design, u-blox 7. If you are not using the same GPS module, you may have to
update the lgw_parse_ubx() function.
* HAL: Added lgw_cnt2gps() and lgw_gps2cnt() functions for SX1301<->GPS time
conversion.
* HAL: Changed serial port configuration for GPS to properly handle binary
messages (UBX).
* HAL: Added a lgw_gps_disable() function to restore serial configuration as
it was before HAL initialization.
* HAL: Fixed packet time on air calculation using the actual packet preamble
size.
* HAL: Adjusted TX_START_DELAY based on the board reference design to ensure
that a packet is sent exactly at 1500µs after the TX trigger (TIMESTAMP or PPS),
with a tolerance of +/- 1µs. This is mandatory to comply with LoRaWAN
specification for Class-B beaconing precise timing.
**WARNING**: This release provides tx start delay values to be used for Semtech
reference designs AP1 and AP2. The HAL automatically detects the board version
by detecting a FPGA or not. If you are using a different reference design or
a different FPGA binary version than the one provided with this release, the
value to be used for TX start delay may be different.
  • Loading branch information
mcoracin committed Feb 17, 2017
1 parent a0040a5 commit a0d4607
Show file tree
Hide file tree
Showing 12 changed files with 704 additions and 185 deletions.
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
4.1.3
5.0.0
93 changes: 76 additions & 17 deletions libloragw/inc/loragw_gps.h
Expand Up @@ -12,7 +12,7 @@
A limited set of module brands/models are supported.
License: Revised BSD License, see LICENSE.TXT file include in the project
Maintainer: Sylvain Miermont
Maintainer: Michael Coracin
*/


Expand All @@ -22,11 +22,11 @@ Maintainer: Sylvain Miermont
/* -------------------------------------------------------------------------- */
/* --- DEPENDANCIES --------------------------------------------------------- */

/* fix an issue between POSIX and C99 */
#define _GNU_SOURCE
#include <stdint.h> /* C99 types */
#include <time.h> /* time library */
#include <termios.h> /* speed_t */
#include <unistd.h> /* ssize_t */

#include "config.h" /* library configuration options (dynamically generated) */

Expand All @@ -40,7 +40,8 @@ Maintainer: Sylvain Miermont
struct tref {
time_t systime; /*!> system time when solution was calculated */
uint32_t count_us; /*!> reference concentrator internal timestamp */
struct timespec utc; /*!> reference UTC time (from GPS) */
struct timespec utc; /*!> reference UTC time (from GPS/NMEA) */
struct timespec gps; /*!> reference GPS time (since 01.Jan.1980) */
double xtal_err; /*!> raw clock error (eg. <1 'slow' XTAL) */
};

Expand All @@ -62,6 +63,7 @@ enum gps_msg {
UNKNOWN, /*!> neutral value */
IGNORED, /*!> frame was not parsed by the system */
INVALID, /*!> system try to parse frame but failed */
INCOMPLETE, /*!> frame parsed was missing bytes */
/* NMEA messages of interest */
NMEA_RMC, /*!> Recommended Minimum data (time + date) */
NMEA_GGA, /*!> Global positioning system fix data (pos + alt) */
Expand All @@ -77,8 +79,8 @@ enum gps_msg {
NMEA_TXT, /*!> Text Transmission */
NMEA_VTG, /*!> Course over ground and Ground speed */
/* uBlox proprietary NMEA messages of interest */
UBX_POSITION, /*!> */
UBX_TIME /*!> */
UBX_NAV_TIMEGPS, /*!> GPS Time Solution */
UBX_NAV_TIMEUTC /*!> UTC Time Solution */
};

/* -------------------------------------------------------------------------- */
Expand All @@ -87,6 +89,10 @@ enum gps_msg {
#define LGW_GPS_SUCCESS 0
#define LGW_GPS_ERROR -1

#define LGW_GPS_MIN_MSG_SIZE (8)
#define LGW_GPS_UBX_SYNC_CHAR (0xB5)
#define LGW_GPS_NMEA_SYNC_CHAR (0x24)

/* -------------------------------------------------------------------------- */
/* --- PUBLIC FUNCTIONS PROTOTYPES ------------------------------------------ */

Expand All @@ -101,6 +107,14 @@ enum gps_msg {
*/
int lgw_gps_enable(char* tty_path, char* gps_familly, speed_t target_brate, int* fd_ptr);

/**
@brief Restore GPS serial configuration and close serial device
@param fd file descriptor on GPS tty
@return success if the function was able to complete
*/
int lgw_gps_disable(int fd);

/**
@brief Parse messages coming from the GPS system (or other GNSS)
Expand All @@ -113,35 +127,52 @@ lgw_gps_get function.
If the lgw_parse_nmea and lgw_gps_get are used in different threads, a mutex
lock must be acquired before calling either function.
*/
enum gps_msg lgw_parse_nmea(char* serial_buff, int buff_size);
enum gps_msg lgw_parse_nmea(const char* serial_buff, int buff_size);

/**
@brief Parse Ublox proprietary messages coming from the GPS system
@param serial_buff pointer to the string to be parsed
@param buff_size maximum string lengths for UBX parsing (incl. null char)
@param msg_size number of bytes parsed as UBX message if found
@return type of frame parsed
The RAW UBX sentences are parsed to a global set of variables shared with the
lgw_gps_get function.
If the lgw_parse_ubx and lgw_gps_get are used in different threads, a mutex
lock must be acquired before calling either function.
*/
enum gps_msg lgw_parse_ubx(const char* serial_buff, size_t buff_size, size_t *msg_size);

/**
@brief Get the GPS solution (space & time) for the concentrator
@param utc pointer to store UTC time, with ns precision (NULL to ignore)
@param gps_time pointer to store GPS time, with ns precision (NULL to ignore)
@param loc pointer to store coordinates (NULL to ignore)
@param err pointer to store coordinates standard deviation (NULL to ignore)
@return success if the chosen elements could be returned
This function read the global variables generated by the NMEA parsing function
lgw_parse_nmea. It returns time and location data in a format that is
exploitable by other functions in that library sub-module.
If the lgw_parse_nmea and lgw_gps_get are used in different threads, a mutex
lock must be acquired before calling either function.
This function read the global variables generated by the NMEA/UBX parsing
functions lgw_parse_nmea/lgw_parse_ubx. It returns time and location data in a
format that is exploitable by other functions in that library sub-module.
If the lgw_parse_nmea/lgw_parse_ubx and lgw_gps_get are used in different
threads, a mutex lock must be acquired before calling either function.
*/
int lgw_gps_get(struct timespec* utc, struct coord_s* loc, struct coord_s* err);
int lgw_gps_get(struct timespec *utc, struct timespec *gps_time, struct coord_s *loc, struct coord_s *err);

/**
@brief Take a timestamp and UTC time and refresh reference for time conversion
@param ref pointer to time reference structure
@param old_ref previous time reference (NULL for initial fix)
@brief Get time and position information from the serial GPS last message received
@param utc UTC time, with ns precision (leap seconds are ignored)
@param gps_time timestamp of last time pulse from the GPS module converted to the UNIX epoch
(leap seconds are ignored)
@param loc location information
@param err location error estimate if supported
@return success if timestamp was read and time reference could be refreshed
Set systime to 0 in ref to trigger initial synchronization.
*/
int lgw_gps_sync(struct tref* ref, uint32_t count_us, struct timespec utc);
int lgw_gps_sync(struct tref *ref, uint32_t count_us, struct timespec utc, struct timespec gps_time);

/**
@brief Convert concentrator timestamp counter value to UTC time
Expand Down Expand Up @@ -171,6 +202,34 @@ transform an absolute UTC time into a matching internal concentrator timestamp.
*/
int lgw_utc2cnt(struct tref ref,struct timespec utc, uint32_t* count_us);

/**
@brief Convert concentrator timestamp counter value to GPS time
@param ref time reference structure required for time conversion
@param count_us internal timestamp counter of the LoRa concentrator
@param gps_time pointer to store GPS time, with ns precision (leap seconds ignored)
@return success if the function was able to convert timestamp to GPS time
This function is typically used when a packet is received to transform the
internal counter-based timestamp in an absolute timestamp with an accuracy in
the order of a millisecond.
*/
int lgw_cnt2gps(struct tref ref, uint32_t count_us, struct timespec* gps_time);

/**
@brief Convert GPS time to concentrator timestamp counter value
@param ref time reference structure required for time conversion
@param gps_time GPS time, with ns precision (leap seconds are ignored)
@param count_us pointer to store internal timestamp counter of LoRa concentrator
@return success if the function was able to convert GPS time to timestamp
This function is typically used when a packet must be sent at an accurate time
(eg. to send a piggy-back response after receiving a packet from a node) to
transform an absolute GPS time into a matching internal concentrator timestamp.
*/
int lgw_gps2cnt(struct tref ref, struct timespec gps_time, uint32_t* count_us);

#endif

/* --- EOF ------------------------------------------------------------------ */
22 changes: 18 additions & 4 deletions libloragw/inc/loragw_hal.h
Expand Up @@ -359,10 +359,24 @@ int lgw_receive(uint8_t max_pkt, struct lgw_pkt_rx_s *pkt_data);
@param pkt_data structure containing the data and metadata for the packet to send
@return LGW_HAL_ERROR id the operation failed, LGW_HAL_SUCCESS else
/!\ When sending a packet, there is a 1.5 ms delay for the analog circuitry to start and be stable (TX_START_DELAY).
In 'timestamp' mode, this is transparent: the modem is started 1.5ms before the user-set timestamp value is reached, the preamble of the packet start right when the internal timestamp counter reach target value.
In 'immediate' mode, the packet is emitted as soon as possible: transferring the packet (and its parameters) from the host to the concentrator takes some time, then there is the TX_START_DELAY, then the packet is emitted.
In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is emitted 1.5ms after a rising edge of the trigger signal. Because there is no way to anticipate the triggering event and start the analog circuitry beforehand, that delay must be taken into account in the protocol.
/!\ When sending a packet, there is a delay (approx 1.5ms) for the analog
circuitry to start and be stable. This delay is adjusted by the HAL depending
on the board version (lgw_i_tx_start_delay_us).
In 'timestamp' mode, this is transparent: the modem is started
lgw_i_tx_start_delay_us microseconds before the user-set timestamp value is
reached, the preamble of the packet start right when the internal timestamp
counter reach target value.
In 'immediate' mode, the packet is emitted as soon as possible: transferring the
packet (and its parameters) from the host to the concentrator takes some time,
then there is the lgw_i_tx_start_delay_us, then the packet is emitted.
In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is
emitted lgw_i_tx_start_delay_us microsenconds after a rising edge of the
trigger signal. Because there is no way to anticipate the triggering event and
start the analog circuitry beforehand, that delay must be taken into account in
the protocol.
*/
int lgw_send(struct lgw_pkt_tx_s pkt_data);

Expand Down
12 changes: 12 additions & 0 deletions libloragw/inc/loragw_reg.h
Expand Up @@ -41,6 +41,12 @@ struct lgw_reg_s {
int32_t dflt; /*!< register default value */
};

enum lgw_brd_version_e {
LGW_BRD_VERSION_1_0, /*!< Gateway v1.0 (no FPGA) */
LGW_BRD_VERSION_1_5, /*!< Gateway v1.5 (with FPGA) */
LGW_BRD_VERSION_UNKNOWN /*!< Unknown */
};

/* -------------------------------------------------------------------------- */
/* --- INTERNAL SHARED FUNCTIONS -------------------------------------------- */

Expand Down Expand Up @@ -408,6 +414,12 @@ int lgw_connect(bool spi_only, uint32_t tx_notch_freq);
*/
int lgw_disconnect(void);

/**
@brief Get LoRa concentrator board version
@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR)
*/
int lgw_brd_version(enum lgw_brd_version_e * brd_version);

/**
@brief Use the soft-reset register to put the concentrator in initial state
@return status of register operation (LGW_REG_SUCCESS/LGW_REG_ERROR)
Expand Down
59 changes: 29 additions & 30 deletions libloragw/readme.md
Expand Up @@ -53,21 +53,24 @@ use the LoRa concentrator:
For an standard application, include only this module.
The use of this module is detailed on the usage section.

/!\ When sending a packet, there is a 1.5 ms delay for the analog circuitry to
start and be stable (TX_START_DELAY).
/!\ When sending a packet, there is a delay (approx 1.5ms) for the analog
circuitry to start and be stable. This delay is adjusted by the HAL depending
on the board version (lgw_i_tx_start_delay_us).

In 'timestamp' mode, this is transparent: the modem is started 1.5ms before the
user-set timestamp value is reached, the preamble of the packet start right when
the internal timestamp counter reach target value.
In 'timestamp' mode, this is transparent: the modem is started
lgw_i_tx_start_delay_us microseconds before the user-set timestamp value is
reached, the preamble of the packet start right when the internal timestamp
counter reach target value.

In 'immediate' mode, the packet is emitted as soon as possible: transferring the
packet (and its parameters) from the host to the concentrator takes some time,
then there is the TX_START_DELAY, then the packet is emitted.
then there is the lgw_i_tx_start_delay_us, then the packet is emitted.

In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is
emitted 1.5ms after a rising edge of the trigger signal. Because there is no
way to anticipate the triggering event and start the analog circuitry
beforehand, that delay must be taken into account in the protocol.
In 'triggered' mode (aka PPS/GPS mode), the packet, typically a beacon, is
emitted lgw_i_tx_start_delay_us microsenconds after a rising edge of the
trigger signal. Because there is no way to anticipate the triggering event and
start the analog circuitry beforehand, that delay must be taken into account in
the protocol.

### 2.2. loragw_reg ###

Expand Down Expand Up @@ -143,25 +146,28 @@ The internal concentrator counter is used to timestamp incoming packets and to
triggers outgoing packets with a microsecond accuracy.
In some cases, it might be useful to be able to transform that internal
timestamp (that is independent for each concentrator running in a typical
networked system) into an absolute UTC time.
networked system) into an absolute GPS time.

In a typical implementation a GPS specific thread will be called, doing the
following things after opening the serial port:

* blocking reads on the serial port (using system read() function)
* parse NMEA sentences (using lgw_parse_nmea)
* parse UBX messages (using lgw_parse_ubx) to get actual native GPS time
* parse NMEA sentences (using lgw_parse_nmea) to get location and UTC time
Note: the RMC sentence gives UTC time, not native GPS time.

And each time an RMC sentence has been received:
And each time an NAV-TIMEGPS UBX message has been received:

* get the concentrator timestamp (using lgw_get_trigcnt, mutex needed to
protect access to the concentrator)
* get the UTC time contained in the NMEA sentence (using lgw_gps_get)
* get the GPS time contained in the UBX message (using lgw_gps_get)
* call the lgw_gps_sync function (use mutex to protect the time reference that
should be a global shared variable).

Then, in other threads, you can simply used that continuously adjusted time
reference to convert internal timestamps to UTC time (using lgw_cnt2utc) or
the other way around (using lgw_utc2cnt).
reference to convert internal timestamps to GPS time (using lgw_cnt2gps) or
the other way around (using lgw_gps2cnt). Inernal concentrator timestamp can
also be converted to/from UTC time using lgw_cnt2utc/lgw_utc2cnt functions.

### 2.6. loragw_radio ###

Expand Down Expand Up @@ -352,20 +358,13 @@ sudo to run all your programs (eg. `sudo ./test_loragw_gps`).

In the current revision, the library only reads data from the serial port,
expecting to receive NMEA frames that are generally sent by GPS receivers as
soon as they are powered up.

The GPS receiver **MUST** send RMC NMEA sentences (starting with "$G<any
character>RMC") shortly after sending a PPS pulse on to allow internal
concentrator timestamps to be converted to absolute UTC time.
If the GPS receiver sends a GGA sentence, the gateway 3D position will also be
available.

The PPS pulse must be sent to the pin 22 of connector CONN400 on the Semtech
FPGA-based nano-concentrator board. Ground is available on pins 2 and 12 of
the same connector.
The pin is loaded by an FPGA internal pull-down, and the signal level coming
in the FPGA must be 3.3V.
Timing is captured on the rising edge of the PPS signal.
soon as they are powered up, and UBX messages which are proprietary to u-blox
modules.

The GPS receiver **MUST** send UBX messages shortly after sending a PPS pulse
on to allow internal concentrator timestamps to be converted to absolute GPS time.
If the GPS receiver sends a GGA NMEA sentence, the gateway 3D position will
also be available.

5. Usage
--------
Expand Down
2 changes: 1 addition & 1 deletion libloragw/src/loragw_fpga.c
Expand Up @@ -94,7 +94,7 @@ const struct lgw_reg_s fpga_regs[LGW_FPGA_TOTALREGS] = {
};

/* -------------------------------------------------------------------------- */
/* --- PRIVATE VARIABLES ---------------------------------------------------- */
/* --- INTERNAL SHARED VARIABLES -------------------------------------------- */

extern void *lgw_spi_target; /*! generic pointer to the SPI device */
extern uint8_t lgw_spi_mux_mode; /*! current SPI mux mode used */
Expand Down

0 comments on commit a0d4607

Please sign in to comment.