From 55e173676ac25227e6117f519a646e813d464764 Mon Sep 17 00:00:00 2001 From: obrl_soil Date: Sat, 12 May 2018 18:45:51 +1000 Subject: [PATCH] moar functions function to reconcile location of paired measurements function to evaluate NMEA sentence checksums speed improvements Internalify helpers with no broader use case --- DESCRIPTION | 2 +- NAMESPACE | 8 +++ NEWS.md | 13 +++-- R/GPS_data.R | 40 +++++++++++--- R/chunk_processing.R | 7 +++ R/import_export.R | 20 ++++--- R/signal_conversion.R | 4 ++ R/spatialise.R | 116 +++++++++++++++++++++++++++++----------- man/em38_pair.Rd | 31 +++++++++++ man/em38_spatial.Rd | 2 +- man/get_cond.Rd | 1 + man/get_loc_data.Rd | 8 +++ man/get_temp.Rd | 1 + man/gpgga_lat.Rd | 1 + man/gpgga_long.Rd | 1 + man/n38_to_points.Rd | 2 +- man/nmea_check.Rd | 25 +++++++++ man/process_cal.Rd | 1 + man/process_comment.Rd | 1 + man/process_fheader.Rd | 1 + man/process_gpgga.Rd | 2 +- man/process_gpgsa.Rd | 2 +- man/process_gpgsv.Rd | 2 +- man/process_gprmc.Rd | 2 +- man/process_gpvtg.Rd | 2 +- man/process_nstat.Rd | 1 + man/process_reading.Rd | 1 + man/process_slheader.Rd | 1 + man/process_timer.Rd | 1 + 29 files changed, 244 insertions(+), 55 deletions(-) create mode 100644 man/em38_pair.Rd create mode 100644 man/nmea_check.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 0f1ecf8..2d4d29d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: em38 Type: Package Title: Process N38 binary files from EM38-MK2 sensors -Version: 0.0.0.9001 +Version: 0.0.0.9002 Authors@R: person("Lauren", "O'Brien", email = "obrlsoilau@gmail.com", role = c('aut', 'cre')) Description: Interprets and decodes the '.N38' file format used by the Geonics EM38-MK2 ground conductivity meter, as described in its diff --git a/NAMESPACE b/NAMESPACE index 33d8014..a499336 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(em38_pair) export(em38_spatial) export(n38_chunk) export(n38_decode) @@ -14,10 +15,17 @@ importFrom(dplyr,mutate) importFrom(dplyr,ungroup) importFrom(purrr,flatten) importFrom(purrr,map) +importFrom(purrr,map2) importFrom(purrr,map_lgl) importFrom(purrr,transpose) importFrom(rlang,.data) importFrom(sf,st_as_sf) +importFrom(sf,st_crs) +importFrom(sf,st_geometry) +importFrom(sf,st_point) +importFrom(sf,st_set_geometry) +importFrom(sf,st_sf) +importFrom(sf,st_sfc) importFrom(stats,complete.cases) importFrom(stats,na.omit) importFrom(tidyr,fill) diff --git a/NEWS.md b/NEWS.md index a13331a..c1fa94c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,15 @@ +# v 0.0.0.9002 + + * Who's got two thumbs and forgot about `@keywords Internal`? \*gestures at self\* + * Function `em38_pair()` added - combines data from manual-mode surveys where horizontal and vertical readings have been taken at each station + * Checksums on NMEA-0183 sentences are evaluated for $GPGGA type, failures are dropped. + # v. 0.0.0.9001 - * wrapper function added - `n38_to_points()` goes from on-disk file to spatial points in one line. - * rebuilt demo data to match demo extdata - * variable name fix in `n38_import()` + * Wrapper function added - `n38_to_points()` goes from on-disk file to spatial points in one hit. + * Rrebuilt demo data to match demo extdata + * Variable name fix in `n38_import()` + * Constrain out_mode better in `em38_spatialise()` # v. 0.0.0.9000 diff --git a/R/GPS_data.R b/R/GPS_data.R index c10c042..0762085 100644 --- a/R/GPS_data.R +++ b/R/GPS_data.R @@ -1,3 +1,31 @@ +#' Calculate NMEA-0183 XOR checksum +#' +#' This function calculates the checksum for NMEA-0183 GPGGA strings. +#' @param string A string with valid NMEA-0183 GPGGA structure. +#' @return Logical; TRUE if checksum is correct. +#' @examples +#' # first GPGGA msg from data('n38_demo') +#' msg_1 <- "$GPGGA,015808.00,2726.53758,S,15126.05255,E,1,08,1.0,365.1,M,39.5,M,,*79" +#' msg_2 <- "$GPG5808.00,2726.53758,S,15126.05255,E,1,08,1.0,365.1,M,39.5,M,,*79" +#' chk_1 <- em38:::nmea_check(string = msg_1) +#' chk_2 <- em38:::nmea_check(string = msg_2) +#' +nmea_check <- function(string = NULL) { + prs <- unlist(strsplit(string, '\\*'))[1] + chk <- unlist(strsplit(string, '\\*'))[2] + # fn still works if starting $ missing already + prs <- gsub('^\\$', '', prs) + prs <- sapply(unlist(strsplit(prs, '*')), charToRaw) + # lower case required for comparison + chk <- tolower(unlist(strsplit(string, '\\*'))[2]) + + prs_chksum <- base::Reduce(xor, prs) + + # good ol' coercion rules, see ?base::Comparison + if(prs_chksum == chk) { TRUE } else { FALSE } +} + + #' Process NMEA-0183 GPGGA messages #' #' This function pulls out position fix data from NMEA-0183 GPGGA strings and dumps it into a list. @@ -6,7 +34,7 @@ #' are given appropriate data types. NB UTC time is returned as POSIXlt, so Sys.date() comes along #' for the ride. #' @examples -#' # first GPGGA msg from decoded demo dataset +#' # first GPGGA msg from data('n38_demo') #' msg_1 <- "$GPGGA,015808.00,2726.53758,S,15126.05255,E,1,08,1.0,365.1,M,39.5,M,,*79" #' gpgga_1 <- em38:::process_gpgga(string = msg_1) #' @importFrom units ud_units @@ -48,7 +76,7 @@ process_gpgga <- function(string = NULL) { # sentence between – but not including – the $ and the * character." # https://rietman.wordpress.com/2008/09/25/how-to-calculate-the-nmea-checksum/ out[['checksum']] <- gsub('^[^\\*]*', '', gga_reading[15]) - # todo: implement validation + # validated later out } @@ -60,7 +88,7 @@ process_gpgga <- function(string = NULL) { #' @return A list containing 6 data elements recorded in NMEA-0183 GPVTG data chunks. Elements #' are given appropriate data types. #' @examples -#' # first GPGVTG msg from decoded demo dataset +#' # first GPGVTG msg from data('n38_demo') #' msg_1 <- "$GPVTG,208.02,T,,M,0.32,N,0.59,K,A*38" #' gpvtg_1 <- em38:::process_gpvtg(string = msg_1) #' @importFrom units ud_units @@ -92,7 +120,7 @@ process_gpvtg <- function(string = NULL) { #' @return A list containing 9 data elements recorded in NMEA-0183 GPRMC data chunks. Elements #' are given appropriate data types. #' @examples -#' # first GPRMC msg from decoded demo dataset +#' # first GPRMC msg from data('n38_demo') #' msg_1 <- "$GPRMC,015808.00,A,2726.53758,S,15126.05255,E,0.32,208.02,160318,,,A*48" #' gprmc_1 <- em38:::process_gprmc(string = msg_1) #' @@ -134,7 +162,7 @@ process_gprmc <- function(string = NULL) { #' @return A list containing n data elements recorded in NMEA-0183 GPGSA data chunks. Elements #' are given appropriate data types. #' @examples -#' # first GPGSA msg from decoded demo dataset +#' # first GPGSA msg from data('n38_demo') #' msg_1 <- "$GPGSA,M,3,05,10,15,16,20,21,26,29,,,,,1.6,1.0,1.2*32" #' gpgsa_1 <- em38:::process_gpgsa(string = msg_1) #' @@ -178,7 +206,7 @@ process_gpgsa <- function(string = NULL) { #' Note also that SNR is receiver-dependant and should only be considered relative to other #' readings in the same dataset. #' @examples -#' # first GPGSV msg from decoded demo dataset +#' # first GPGSV msg from data('n38_demo') #' msg_1 <- "$GPGSV,3,1,11,05,14,138,46,10,14,316,37,12,04,012,,13,24,100,*76" #' gpgsv_1 <- em38:::process_gpgsv(string = msg_1) #' diff --git a/R/chunk_processing.R b/R/chunk_processing.R index 15032c7..28a7431 100644 --- a/R/chunk_processing.R +++ b/R/chunk_processing.R @@ -4,6 +4,7 @@ #' internal function with no wider use case. #' @param file_header A matrix with 2 rows and 25 columns, produced by \code{\link{n38_import}}. #' @return A list containing 9 information elements recorded in N38 file headers. +#' @keywords Internal #' @examples #' data('n38_demo') #' n38_chunked <- n38_chunk(n38_demo) @@ -53,6 +54,7 @@ process_fheader <- function(file_header = NULL) { #' This is an internal function with no wider use case. #' @param survline_header A matrix with 4 rows and 25 columns, produced by \code{\link{n38_import}}. #' @return A list containing 5 information elements recorded in N38 survey line headers. +#' @keywords Internal #' @examples #' data('n38_demo') #' n38_chunked <- n38_chunk(n38_demo) @@ -82,6 +84,7 @@ process_slheader <- function(survline_header = NULL) { #' This is an internal function with no wider use case. #' @param cal_row A matrix with 1 row and 25 columns, produced by \code{\link{n38_import}}. #' @return A list containing 3 information elements recorded in N38 calibration rows. +#' @keywords Internal #' @examples #' data('n38_demo') #' n38_chunked <- n38_chunk(n38_demo) @@ -104,6 +107,7 @@ process_cal <- function(cal_row = NULL) { #' @param timer_rel A matrix with 1 row and 25 columns, produced by \code{\link{n38_import}}. #' @return A list containing 2 information elements recorded in N38 timer relation rows. Note that #' time will be returned in the local timezone. +#' @keywords Internal #' @examples #' data('n38_demo') #' n38_chunked <- n38_chunk(n38_demo) @@ -126,6 +130,7 @@ process_timer <- function(timer_rel = NULL) { #' is an internal function with no wider use case. #' @param reading A matrix with 1 row and 25 columns, produced by \code{\link{n38_import}}. #' @return A list containing 10 data elements recorded in N38 instrument reading rows. +#' @keywords Internal #' @examples #' data('n38_demo') #' n38_chunked <- n38_chunk(n38_demo) @@ -180,6 +185,7 @@ process_reading <- function(reading = NULL) { #' This is an internal function with no wider use case. #' @param comment A matrix with 1 row and 25 columns, produced by \code{\link{n38_import}}. #' @return A list containing 2 data elements recorded in N38 comment rows. +#' @keywords Internal #' @examples \dontrun{ #' data('n38_demo') #' n38_chunked <- n38_chunk(n38_demo) @@ -201,6 +207,7 @@ process_comment <- function(comment = NULL) { #' This is an internal function with no wider use case. #' @param nstat A matrix with 1 row and 25 columns, produced by \code{\link{n38_import}}. #' @return A list containing 2 data elements recorded in N38 new station rows. +#' @keywords Internal #' @examples \dontrun{ #' data('n38_demo') #' n38_chunked <- n38_chunk(n38_demo) diff --git a/R/import_export.R b/R/import_export.R index d810d18..18bb937 100644 --- a/R/import_export.R +++ b/R/import_export.R @@ -206,14 +206,14 @@ n38_decode <- function(chunks = NULL) { # ditch the `#` newline signifiers and whitespace, convert to string locs <- lapply(locs, function(x) { x[x == '#'] <- NA - x[x == ' '] <- NA + x[x == ' '] <- NA paste0(na.omit(x), collapse = '') }) - # The following ditches checksum fails - these start with ? not @ and use " instead of # as an - # internal newline signifier. Also handled are cases where GPS messages can occasionally get cut - # off after the message comes through but before the timestamp does (this where someone hits - # pause at the wrong time) + # The following ditches checksum fails that have already been flagged by GPS software. These + # start with ? not @ and use " instead of # as an internal newline signifier. Also handled are + # cases where GPS messages can occasionally get cut off after the message comes through but + # before the timestamp does (this where someone hits pause at the wrong time) keep <- purrr::map_lgl(locs, function(x) grepl('^@.+\\!', x)) locs <- locs[keep] @@ -224,8 +224,14 @@ n38_decode <- function(chunks = NULL) { type <- substr(x, 3, 7) bang <- as.integer(gregexpr('!', x)) msg <- substr(x, 9, bang - 1) - ts <- as.integer(substr(x, bang + 1, nchar(x))) - list('TYPE' = type, 'MESSAGE' = msg, 'timestamp_ms' = ts) + # only checking GPGGA messages to save time + chks <- if(type == 'GPGGA') { + nmea_check(substr(x, 2, bang - 1)) + } else { + NA + } + ts <- as.integer(substr(x, bang + 1, nchar(x))) + list('TYPE' = type, 'MESSAGE' = msg, 'CHKSUM' = chks, 'timestamp_ms' = ts) }) out <- purrr::transpose(out) diff --git a/R/signal_conversion.R b/R/signal_conversion.R index bdd77ab..96aa66e 100644 --- a/R/signal_conversion.R +++ b/R/signal_conversion.R @@ -6,6 +6,7 @@ #' isolation. #' @param signal Integer. #' @return Numeric; uncalibrated instrument reading. +#' @keywords Internal #' @examples #' channel_1_or_3 <- em38:::get_cond(30456) #' channel_2 <- em38:::get_cond(30456) * 0.00720475 @@ -20,6 +21,7 @@ get_cond <- function(signal = NULL) { #' This function calculates temperature from an EM38-MK2 signal recieved on Channel 5 or 6. #' @param signal Integer. #' @return Temperature in degrees C +#' @keywords Internal #' @examples #' channel_5 <- em38:::get_temp(30456) #' @@ -38,6 +40,7 @@ get_temp <- function(signal = NULL) { #' @return Numeric, latitude in decimal degrees. #' @note Inputting a numeric to lat will give incorrect results for latitude -10 < x < 10 due to #' loss of leading zero(s). +#' @keywords Internal #' @examples #' lat <- em38:::gpgga_lat('2729.10198', 'S') #' @@ -67,6 +70,7 @@ gpgga_lat <- function(lat = NULL, dir = NULL) { #' @return Numeric, longitude in decimal degrees. #' @note Inputting a numeric to long will give incorrect results for longitude -100 < x < 100 due to #' loss of leading zero(s). +#' @keywords Internal #' @examples #' lat <- em38:::gpgga_long('15257.5556', 'E') #' diff --git a/R/spatialise.R b/R/spatialise.R index d6a103b..7a30440 100644 --- a/R/spatialise.R +++ b/R/spatialise.R @@ -5,10 +5,18 @@ #' @param block Data frame holding GPS message data, usually a subset of $location_data in a #' decoded n38 object #' @return data frame with a single row +#' @keywords Internal +#' @examples +#' data('n38_demo') +#' n38_chunks <- n38_chunk(n38_demo) +#' n38_decoded <- n38_decode(n38_chunks) +#' loc_1 <- em38:::get_loc_data(n38_decoded$survey_line_1$location_data[1:7, ]) #' get_loc_data <- function(block = NULL) { + # all the interesting stuff is in the first one - gpgga <- process_gpgga(paste0('$GPGGA,', block$MESSAGE[block$TYPE == 'GPGGA'])) + gpgga <- process_gpgga(paste0(block[, c('TYPE', 'MESSAGE')][block$TYPE == 'GPGGA',], + collapse = ',')) # keeping these for later but no need to decode the entire block just yet #gpvtg <- em38:::process_gpvtg(paste0('$GPVTG,', x$MESSAGE[x$TYPE == 'GPVTG'])) @@ -21,12 +29,13 @@ get_loc_data <- function(block = NULL) { data.frame('LATITUDE' = gpgga[['latitude']], 'LONGITUDE' = gpgga[['longitude']], 'HDOP' = gpgga[['HDOP']], + 'CHKSUM' = block$CHKSUM[block$TYPE == 'GPGGA'], 'timestamp_ms' = block$timestamp_ms[block$TYPE == 'GPGGA']) # if this fails, suspect #1 is multiple GPGGA messages in block # suspect #2 is a GPS device that doesn't return GPGGA (e.g. GLGPA, or GPRMC) # will defo need to add handlers for GLONASS and other systems but need test data -} + } #' Spatialise EM38 data #' @@ -38,7 +47,7 @@ get_loc_data <- function(block = NULL) { #' Horizontal data, never both. #' @return An sf data frame with sfc_POINT geometry. WGS84 projection. If the n38_decoded object #' contains more than one survey line, a list of sf objects is returned - one for each line. -#' @note Input n38_decoded object should be of survey type 'GPS' and record type 'auto'. If not, the +#' @note Input n38_decoded object should be of survey type 'GPS'. If not, the #' function will fail gracefully by returning a list of reasons why the data could not be #' converted to points. #' @examples @@ -79,11 +88,9 @@ em38_spatial <- function(n38_decoded = NULL, } else { # pull out location data and group it by repeating sequence of records - loc <- n38_decoded[[i]][['location_data']] - loc <- dplyr::mutate(loc, - lag_chk = ifelse(.data$TYPE == .data$TYPE[1], T, F), - group = cumsum(.data$lag_chk) - ) + loc <- n38_decoded[[i]][['location_data']] + loc$lag_chk <- ifelse(loc$TYPE == loc$TYPE[1], TRUE, FALSE) + loc$group <- cumsum(loc$lag_chk) loc_s <- split(loc, loc$group) # drop any chunks that don't have a GPGGA message (usually a start/pause error) keep <- @@ -100,6 +107,9 @@ em38_spatial <- function(n38_decoded = NULL, }) loc_f <- do.call('rbind', loc_s) + # remove checksum failures + loc_f <- loc_f[loc_f$CHKSUM == TRUE, ] + # filter out low-precision locations and also some dud readings (checksum passed # but message still missing essential data) # note that this is not done by the nmea parser on purpose - prefer record sequence @@ -139,11 +149,22 @@ em38_spatial <- function(n38_decoded = NULL, all_data$timestamp_ms - dplyr::lag(all_data$timestamp_ms) all_data$TS_LAG_AD[is.na(all_data$TS_LAG_AD)] <- 0 + # fill a few values in so all instrument readings have a 'from' and 'to' for interpolation + all_data <- tidyr::fill( + all_data, + .data$LATITUDE, + .data$LONGITUDE, + .data$LEAD_LAT, + .data$LEAD_LONG, + .data$TS_LAG, + .direction = 'down' + ) + # group recombined data so that GPS reading(s) and following instrument reading(s) are # together # grouping by sequence is hard and this seems awful but whateverrrrr - grp <- rle(ifelse(is.na(all_data$LATITUDE), 1, 0))$lengths + grp <- rle(ifelse(is.na(all_data$HDOP), 1, 0))$lengths grp <- data.frame('rle' = grp) grp$grp <- c(rep(1:(nrow(grp) / 2), each = 2), ceiling(nrow(grp) / 2)) grp <- split(grp, grp$grp) @@ -158,17 +179,6 @@ em38_spatial <- function(n38_decoded = NULL, y = grp )) - # fill a few values in so all instrument readings have a 'from' and 'to' for interpolation - all_data <- tidyr::fill( - all_data, - .data$LATITUDE, - .data$LONGITUDE, - .data$LEAD_LAT, - .data$LEAD_LONG, - .data$TS_LAG, - .direction = 'down' - ) - # get cumulative time lag within each group (effectively distance between last gps reading # and current instrument reading) all_data$ind3 = ifelse(is.na(all_data$HDOP), 1, 0) @@ -182,16 +192,13 @@ em38_spatial <- function(n38_decoded = NULL, # use 2D linear interpolation to get locations for instrument readings # no point getting geodetic here, we're generally working at < 1m distances # https://math.stackexchange.com/questions/1918743/how-to-interpolate-points-between-2-points#1918765 - all_data <- dplyr::mutate( - all_data, - NEW_LAT = .data$LATITUDE + ( - .data$TS_NOW / .data$TS_LAG * - (.data$LEAD_LAT - .data$LATITUDE) - ), - NEW_LONG = .data$LONGITUDE + ( - .data$TS_NOW / .data$TS_LAG * - (.data$LEAD_LONG - .data$LONGITUDE) - ) + all_data$NEW_LAT <- all_data$LATITUDE + ( + all_data$TS_NOW / all_data$TS_LAG * + (all_data$LEAD_LAT - all_data$LATITUDE) + ) + all_data$NEW_LONG <- all_data$LONGITUDE + ( + all_data$TS_NOW / all_data$TS_LAG * + (all_data$LEAD_LONG - all_data$LONGITUDE) ) # filter to just keep instrument readings and interpolated locations @@ -237,7 +244,7 @@ em38_spatial <- function(n38_decoded = NULL, #' Horizontal data, never both. #' @return An sf data frame with sfc_POINT geometry. WGS84 projection. If the n38_decoded object #' contains more than one survey line, a list of sf objects is returned - one for each line. -#' @note Input file should be of survey type 'GPS' and record type 'auto'. If not, the +#' @note Input file should be of survey type 'GPS'. If not, the #' function will fail gracefully by returning reasons why the data could not be #' converted to points. #' @examples @@ -254,3 +261,50 @@ n38_to_points <- function(path = NULL, hdop_filter = 3, em38_spatial(n38_decoded = dec, hdop_filter = hdop_filter, out_mode = out_mode) } + +#' Reconcile locations of paired data +#' +#' Where paired horizontal and vertical readings have been taken during a 'manual' mode survey, the +#' first and second readings at each station should have the same location. The nature of the device +#' logging generally precludes this from happening by default, especially with high-frequency GPS +#' recording. This function reconciles the locations of such paired datasets after they have been +#' generated using \code{\link{em38_spatial}} or \code{\link{n38_to_points}}. +#' @param horizontal_data spatial point dataframe produced by \code{\link{em38_spatial}} or +#' \code{\link{n38_to_points}} with `out_mode = 'Horizontal`. +#' @param vertical_data spatial point dataframe produced by \code{\link{em38_spatial}} or +#' \code{\link{n38_to_points}} with `out_mode = 'Vertical`. +#' @return An sf data frame with sfc_POINT geometry. WGS84 projection. Output locations are averages +#' of input locations. Data columns are labelled as horizontal or vertical. +#' @note Input data should be of survey type 'GPS' and record type 'manual'. Both input datasets +#' should have the same number of rows, with row 1 of horizontal_data paired with row_1 of +#' vertical_data. +#' @importFrom purrr map2 +#' @importFrom sf st_crs st_geometry st_point st_set_geometry st_sfc st_sf +#' @export +#' +em38_pair <- function(horizontal_data = NULL, vertical_data = NULL) { + # take paired horizontal and vertical em38 readings and reconcile their locations + geom_h <- st_geometry(horizontal_data) + geom_v <- st_geometry(vertical_data) + + pts <- purrr::map2(.x = geom_h, .y = geom_v, function(.x, .y) { + out_long <- mean(c(as.vector(.x)[1], + as.vector(.y)[1])) + out_lat <- mean(c(as.vector(.x)[2], + as.vector(.y)[2])) + + st_point(c(out_long, out_lat)) + }) + + new_geom <- st_sfc(pts, crs = st_crs(geom_h)$proj4string) + + # combine in output + hdata <- st_set_geometry(horizontal_data, NULL) + names(hdata) <- paste0('H_', names(hdata)) + vdata <- st_set_geometry(vertical_data, NULL) + names(vdata) <- paste0('V_', names(vdata)) + all_data <- cbind(hdata, vdata) + + st_sf(all_data, 'geometry' = new_geom) + +} diff --git a/man/em38_pair.Rd b/man/em38_pair.Rd new file mode 100644 index 0000000..d960f63 --- /dev/null +++ b/man/em38_pair.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spatialise.R +\name{em38_pair} +\alias{em38_pair} +\title{Reconcile locations of paired data} +\usage{ +em38_pair(horizontal_data = NULL, vertical_data = NULL) +} +\arguments{ +\item{horizontal_data}{spatial point dataframe produced by \code{\link{em38_spatial}} or +\code{\link{n38_to_points}} with `out_mode = 'Horizontal`.} + +\item{vertical_data}{spatial point dataframe produced by \code{\link{em38_spatial}} or +\code{\link{n38_to_points}} with `out_mode = 'Vertical`.} +} +\value{ +An sf data frame with sfc_POINT geometry. WGS84 projection. Output locations are averages + of input locations. Data columns are labelled as horizontal or vertical. +} +\description{ +Where paired horizontal and vertical readings have been taken during a 'manual' mode survey, the +first and second readings at each station should have the same location. The nature of the device +logging generally precludes this from happening by default, especially with high-frequency GPS +recording. This function reconciles the locations of such paired datasets after they have been +generated using \code{\link{em38_spatial}} or \code{\link{n38_to_points}}. +} +\note{ +Input data should be of survey type 'GPS' and record type 'manual'. Both input datasets + should have the same number of rows, with row 1 of horizontal_data paired with row_1 of + vertical_data. +} diff --git a/man/em38_spatial.Rd b/man/em38_spatial.Rd index 4e12b76..5f7185a 100644 --- a/man/em38_spatial.Rd +++ b/man/em38_spatial.Rd @@ -24,7 +24,7 @@ An sf data frame with sfc_POINT geometry. WGS84 projection. If the n38_decoded o This function processes a decoded N38 record into a point spatial dataset. } \note{ -Input n38_decoded object should be of survey type 'GPS' and record type 'auto'. If not, the +Input n38_decoded object should be of survey type 'GPS'. If not, the function will fail gracefully by returning a list of reasons why the data could not be converted to points. } diff --git a/man/get_cond.Rd b/man/get_cond.Rd index 52f9cd7..9d6eed1 100644 --- a/man/get_cond.Rd +++ b/man/get_cond.Rd @@ -24,3 +24,4 @@ channel_2 <- em38:::get_cond(30456) * 0.00720475 channel_4 <- em38:::get_cond(30456) * 0.028819 } +\keyword{Internal} diff --git a/man/get_loc_data.Rd b/man/get_loc_data.Rd index 1d3af76..81d5dfa 100644 --- a/man/get_loc_data.Rd +++ b/man/get_loc_data.Rd @@ -17,3 +17,11 @@ data frame with a single row This function decodes GPS data for use in em38_spatial and returns it along with its timestamp data. } +\examples{ +data('n38_demo') +n38_chunks <- n38_chunk(n38_demo) +n38_decoded <- n38_decode(n38_chunks) +loc_1 <- em38:::get_loc_data(n38_decoded$survey_line_1$location_data[1:7, ]) + +} +\keyword{Internal} diff --git a/man/get_temp.Rd b/man/get_temp.Rd index a8beb82..e8b438b 100644 --- a/man/get_temp.Rd +++ b/man/get_temp.Rd @@ -19,3 +19,4 @@ This function calculates temperature from an EM38-MK2 signal recieved on Channel channel_5 <- em38:::get_temp(30456) } +\keyword{Internal} diff --git a/man/gpgga_lat.Rd b/man/gpgga_lat.Rd index c0f88d3..a287ae8 100644 --- a/man/gpgga_lat.Rd +++ b/man/gpgga_lat.Rd @@ -27,3 +27,4 @@ loss of leading zero(s). lat <- em38:::gpgga_lat('2729.10198', 'S') } +\keyword{Internal} diff --git a/man/gpgga_long.Rd b/man/gpgga_long.Rd index fe1911a..daa59e2 100644 --- a/man/gpgga_long.Rd +++ b/man/gpgga_long.Rd @@ -27,3 +27,4 @@ loss of leading zero(s). lat <- em38:::gpgga_long('15257.5556', 'E') } +\keyword{Internal} diff --git a/man/n38_to_points.Rd b/man/n38_to_points.Rd index 7d65a76..7aba81d 100644 --- a/man/n38_to_points.Rd +++ b/man/n38_to_points.Rd @@ -27,7 +27,7 @@ This is a wrapper function that processes a raw on-disk N38 file into an sf poin dataset. } \note{ -Input file should be of survey type 'GPS' and record type 'auto'. If not, the +Input file should be of survey type 'GPS'. If not, the function will fail gracefully by returning reasons why the data could not be converted to points. } diff --git a/man/nmea_check.Rd b/man/nmea_check.Rd new file mode 100644 index 0000000..342a2c8 --- /dev/null +++ b/man/nmea_check.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/GPS_data.R +\name{nmea_check} +\alias{nmea_check} +\title{Calculate NMEA-0183 XOR checksum} +\usage{ +nmea_check(string = NULL) +} +\arguments{ +\item{string}{A string with valid NMEA-0183 GPGGA structure.} +} +\value{ +Logical; TRUE if checksum is correct. +} +\description{ +This function calculates the checksum for NMEA-0183 GPGGA strings. +} +\examples{ +# first GPGGA msg from data('n38_demo') +msg_1 <- "$GPGGA,015808.00,2726.53758,S,15126.05255,E,1,08,1.0,365.1,M,39.5,M,,*79" +msg_2 <- "$GPG5808.00,2726.53758,S,15126.05255,E,1,08,1.0,365.1,M,39.5,M,,*79" +chk_1 <- em38:::nmea_check(string = msg_1) +chk_2 <- em38:::nmea_check(string = msg_2) + +} diff --git a/man/process_cal.Rd b/man/process_cal.Rd index 9cc89ed..176c5aa 100644 --- a/man/process_cal.Rd +++ b/man/process_cal.Rd @@ -22,3 +22,4 @@ n38_chunked <- n38_chunk(n38_demo) n38_cal1 <- em38:::process_cal(n38_chunked[['survey_line_1']][['cal_data']][1, ]) } +\keyword{Internal} diff --git a/man/process_comment.Rd b/man/process_comment.Rd index 148977a..8c5fa42 100644 --- a/man/process_comment.Rd +++ b/man/process_comment.Rd @@ -25,3 +25,4 @@ n38_c1 <- em38:::process_comment(n38_chunked[['survey_line_1']][['comments']][1, } } +\keyword{Internal} diff --git a/man/process_fheader.Rd b/man/process_fheader.Rd index 3a5dafb..ca3d2b7 100644 --- a/man/process_fheader.Rd +++ b/man/process_fheader.Rd @@ -22,3 +22,4 @@ n38_chunked <- n38_chunk(n38_demo) n38_fh <- em38:::process_fheader(n38_chunked[['file_header']]) } +\keyword{Internal} diff --git a/man/process_gpgga.Rd b/man/process_gpgga.Rd index 56ba8a9..375b547 100644 --- a/man/process_gpgga.Rd +++ b/man/process_gpgga.Rd @@ -18,7 +18,7 @@ for the ride. This function pulls out position fix data from NMEA-0183 GPGGA strings and dumps it into a list. } \examples{ -# first GPGGA msg from decoded demo dataset +# first GPGGA msg from data('n38_demo') msg_1 <- "$GPGGA,015808.00,2726.53758,S,15126.05255,E,1,08,1.0,365.1,M,39.5,M,,*79" gpgga_1 <- em38:::process_gpgga(string = msg_1) } diff --git a/man/process_gpgsa.Rd b/man/process_gpgsa.Rd index 2ca4826..3c4c2bf 100644 --- a/man/process_gpgsa.Rd +++ b/man/process_gpgsa.Rd @@ -18,7 +18,7 @@ This function pulls out GPS DOP and active satellites data from NMEA-0183 GPGSA and dumps it into a list. } \examples{ -# first GPGSA msg from decoded demo dataset +# first GPGSA msg from data('n38_demo') msg_1 <- "$GPGSA,M,3,05,10,15,16,20,21,26,29,,,,,1.6,1.0,1.2*32" gpgsa_1 <- em38:::process_gpgsa(string = msg_1) diff --git a/man/process_gpgsv.Rd b/man/process_gpgsv.Rd index 44a0ac2..10a234f 100644 --- a/man/process_gpgsv.Rd +++ b/man/process_gpgsv.Rd @@ -22,7 +22,7 @@ This function pulls out satellites in view data from NMEA-0183 GPGSV strings and dumps it into a list. } \examples{ -# first GPGSV msg from decoded demo dataset +# first GPGSV msg from data('n38_demo') msg_1 <- "$GPGSV,3,1,11,05,14,138,46,10,14,316,37,12,04,012,,13,24,100,*76" gpgsv_1 <- em38:::process_gpgsv(string = msg_1) diff --git a/man/process_gprmc.Rd b/man/process_gprmc.Rd index 6855d06..365989f 100644 --- a/man/process_gprmc.Rd +++ b/man/process_gprmc.Rd @@ -18,7 +18,7 @@ This function pulls out reccommended minimum sentence data from NMEA-0183 GPRMC and dumps it into a list. } \examples{ -# first GPRMC msg from decoded demo dataset +# first GPRMC msg from data('n38_demo') msg_1 <- "$GPRMC,015808.00,A,2726.53758,S,15126.05255,E,0.32,208.02,160318,,,A*48" gprmc_1 <- em38:::process_gprmc(string = msg_1) diff --git a/man/process_gpvtg.Rd b/man/process_gpvtg.Rd index 8ce4fd6..f0f01ae 100644 --- a/man/process_gpvtg.Rd +++ b/man/process_gpvtg.Rd @@ -18,7 +18,7 @@ This function pulls out track made good and speed over ground data from NMEA-018 and dumps it into a list. } \examples{ -# first GPGVTG msg from decoded demo dataset +# first GPGVTG msg from data('n38_demo') msg_1 <- "$GPVTG,208.02,T,,M,0.32,N,0.59,K,A*38" gpvtg_1 <- em38:::process_gpvtg(string = msg_1) } diff --git a/man/process_nstat.Rd b/man/process_nstat.Rd index f7d246e..d2da9ef 100644 --- a/man/process_nstat.Rd +++ b/man/process_nstat.Rd @@ -25,3 +25,4 @@ n38_ns1 <- em38:::process_nstat(n38_chunked[['survey_line_1']][['new_stations']] } } +\keyword{Internal} diff --git a/man/process_reading.Rd b/man/process_reading.Rd index 863b51e..c608040 100644 --- a/man/process_reading.Rd +++ b/man/process_reading.Rd @@ -22,3 +22,4 @@ n38_chunked <- n38_chunk(n38_demo) n38_r1 <- em38:::process_reading(n38_chunked[['survey_line_1']][['reading_data']][1, ]) } +\keyword{Internal} diff --git a/man/process_slheader.Rd b/man/process_slheader.Rd index 27c3fe1..f2f1ba6 100644 --- a/man/process_slheader.Rd +++ b/man/process_slheader.Rd @@ -22,3 +22,4 @@ n38_chunked <- n38_chunk(n38_demo) n38_slh <- em38:::process_slheader(n38_chunked[['survey_line_1']][['sl_header']]) } +\keyword{Internal} diff --git a/man/process_timer.Rd b/man/process_timer.Rd index 9aa9e36..207d4cd 100644 --- a/man/process_timer.Rd +++ b/man/process_timer.Rd @@ -23,3 +23,4 @@ n38_chunked <- n38_chunk(n38_demo) n38_t <- em38:::process_timer(n38_chunked[['survey_line_1']][['timer_data']]) } +\keyword{Internal}