diff --git a/DESCRIPTION b/DESCRIPTION index e5eb1cd..0f1ecf8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,10 +1,10 @@ Package: em38 Type: Package Title: Process N38 binary files from EM38-MK2 sensors -Version: 0.0.0.9000 +Version: 0.0.0.9001 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 electromagnetic sensing instrument, as described in its + Geonics EM38-MK2 ground conductivity meter, as described in its manual. Conversion to '.M38' format or an 'sf' point dataset is supported. This package offers an alternative to the existing format conversion software, which lacks a command-line interface and thus cannot be easily diff --git a/NAMESPACE b/NAMESPACE index 491ffaa..33d8014 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,6 +5,7 @@ export(n38_chunk) export(n38_decode) export(n38_import) export(n38_to_m38) +export(n38_to_points) importFrom(dplyr,bind_rows) importFrom(dplyr,group_by) importFrom(dplyr,lag) diff --git a/NEWS.md b/NEWS.md index 33d84fe..a13331a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,8 @@ +# 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()` # v. 0.0.0.9000 diff --git a/R/import_export.R b/R/import_export.R index fe3a07a..d810d18 100644 --- a/R/import_export.R +++ b/R/import_export.R @@ -1,17 +1,17 @@ #' Import EM38 data #' #' This function reads N38 binary files into R and does some minimal pre-processing. -#' @param x A file path pointing to a valid *.N38 file, produced by a Geonics EM38-MK2 conductivity -#' sensor connected to an Allegra CX datalogger (and optionally, a GPS device). +#' @param path A file path pointing to a valid *.N38 file, produced by a Geonics EM38-MK2 conductivity +#' sensor connected to an Allegra CX or Archer datalogger (and optionally, a GPS device). #' @return A matrix with n rows and 25 columns, containing raw bytes. #' @examples #' n38_mat <- n38_import(system.file("extdata", "em38_demo.n38", package = "em38")) #' @export #' -n38_import <- function(x = NULL) { +n38_import <- function(path = NULL) { - n38_con <- file(x, open = 'rb') - n38_raw <- readBin(n38_con, what = raw(), n = file.size(x)) + n38_con <- file(path, open = 'rb') + n38_raw <- readBin(n38_con, what = raw(), n = file.size(path)) close(n38_con) n38_mat <- matrix(n38_raw, ncol = 26, byrow = TRUE) diff --git a/R/spatialise.R b/R/spatialise.R index 29c2987..d6a103b 100644 --- a/R/spatialise.R +++ b/R/spatialise.R @@ -28,7 +28,7 @@ get_loc_data <- function(block = NULL) { } -#' Spatialise data +#' Spatialise EM38 data #' #' This function processes a decoded N38 record into a point spatial dataset. #' @param n38_decoded Nested list output by n38_decode @@ -38,7 +38,9 @@ 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 must be of survey type 'GPS' and record type 'auto'. +#' @note Input n38_decoded object should be of survey type 'GPS' and record type 'auto'. If not, the +#' function will fail gracefully by returning a list of reasons why the data could not be +#' converted to points. #' @examples #' data('n38_demo') #' n38_chunks <- n38_chunk(n38_demo) @@ -64,7 +66,11 @@ em38_spatial <- function(n38_decoded = NULL, readings <- n38_decoded[[i]][['reading_data']] # filter readings to chosen dipole mode - readings <- readings[readings$mode == out_mode, ] + readings <- if(out_mode %in% c('Vertical', 'Horizontal')) { + readings[readings$mode == out_mode, ] + } else { + return('Please check the value of out_mode.') + } # if no readings of the chosen out_mode exist in this sl, if(nrow(readings) == 0) { @@ -217,3 +223,34 @@ em38_spatial <- function(n38_decoded = NULL, } } + +#' Import and convert N38 logfiles to points +#' +#' This is a wrapper function that processes a raw on-disk N38 file into an sf point spatial +#' dataset. +#' @param path A file path pointing to a valid *.N38 file, produced by a Geonics EM38-MK2 +#' conductivity sensor connected to an Allegra CX or Archer datalogger (and optionally, a GPS +#' device). +#' @param hdop_filter Numeric, discard GPS data where the Horizontal Dilution of Precision is +#' greater than this number. Defaults to 3 metres. Set to NULL to keep all readings. +#' @param out_mode Character, em38 dipole mode. Output dataset can only contain Vertical or +#' 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 +#' function will fail gracefully by returning reasons why the data could not be +#' converted to points. +#' @examples +#' vert_points <- +#' n38_to_points(path = system.file("extdata", "em38_demo.n38", package = "em38"), +#' hdop_filter = 3, out_mode = 'Vertical') +#' @export +n38_to_points <- function(path = NULL, hdop_filter = 3, + out_mode = c('Vertical', 'Horizontal')) { + mat <- n38_import(path) + chnk <- n38_chunk(mat) + dec <- n38_decode(chnk) + + em38_spatial(n38_decoded = dec, hdop_filter = hdop_filter, out_mode = out_mode) + +} diff --git a/README.Rmd b/README.Rmd index 95de8bd..b1d040a 100644 --- a/README.Rmd +++ b/README.Rmd @@ -20,35 +20,49 @@ em38 translates *.n38 binary files generated by the [Geonics EM38-MKII ground co ## Installation -Install development versions from github with +Install from github with -```{r 'instalation', eval = FALSE} +```{r 'installation', eval = FALSE} library(devtools) install_github("obrl-soil/em38") ``` -This package is in early development phase. Improving its reliability requires access to a wide variety of test datasets, so if you have an *.n38 file that fails to decode correctly, please consider sending it to me. I have been able to test decoding on around 50 sample files from two different EM38-MKII devices, but could always use more. The device and its logger have a large number of possible setting combinations, and have not seen all of them. Additionally, a third-party GPS needs to be attached to the device and its data-logger, and GPS output data are notoriously variable by brand and model. +This package is in early development phase. Improving its reliability requires access to a wide variety of test datasets, so if you have an *.n38 file that fails to decode correctly, please consider sending it to me. I have been able to test decoding on around 50 sample files from two different EM38-MKII devices, but could always use more. The device and its logger have a large number of possible setting combinations, and I have not seen all of them. Additionally, a third-party GPS needs to be attached to the device and its data-logger, and GPS output data are notoriously variable by brand and model. ## Usage em38 is accompanied by a demo dataset: -```{r example} +```{r 'example', message=FALSE} library(em38) + +# all-in-one wrapper function: +em38_sf <- n38_to_points(path = system.file("extdata", "em38_demo.n38", package = "em38"), + hdop_filter = 3, out_mode = 'Vertical') + + +# Plot spatialised output (calibrated conductivity for coil separation 0.5m) +plot(em38_sf['cond_05'], pch = 20) +``` + +Its surprisingly difficult to walk in a straight line across a paddock :no_mouth: + +If you want to look at the intermediate data more closely, + +```{r 'longwayround'} # import binary file as raw() type matrix n38_mat <- n38_import(system.file("extdata", "em38_demo.n38", package = "em38")) # break matrix into sections according to file spec n38_chunks <- n38_chunk(n38_mat) # decode matrix chunks into useable data n38_decoded <- n38_decode(n38_chunks) - # output an m38 file (comparable to official decode) m38_example <- n38_to_m38(n38_decoded) # write to file as e.g. write(m38_example, paste0('m38_from_R_', Sys.Date(), '.m38')) # spatialise the decoded data -em38_sf <- em38_spatial(n38_decoded, out_mode = 'Vertical') +em38_sf <- em38_spatial(n38_decoded, hdop_filter = 3, out_mode = 'Vertical') -# Plot spatialised output (calibrated conductivity for coil separation 0.5m) -plot(em38_sf['cond_05'], pch = 20) ``` + +*** diff --git a/README.md b/README.md index e0d99ee..2d7c632 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ em38 translates \*.n38 binary files generated by the [Geonics EM38-MKII ground c Installation ------------ -Install development versions from github with +Install from github with ``` r library(devtools) install_github("obrl-soil/em38") ``` -This package is in early development phase. Improving its reliability requires access to a wide variety of test datasets, so if you have an \*.n38 file that fails to decode correctly, please consider sending it to me. I have been able to test decoding on around 50 sample files from two different EM38-MKII devices, but could always use more. The device and its logger have a large number of possible setting combinations, and have not seen all of them. Additionally, a third-party GPS needs to be attached to the device and its data-logger, and GPS output data are notoriously variable by brand and model. +This package is in early development phase. Improving its reliability requires access to a wide variety of test datasets, so if you have an \*.n38 file that fails to decode correctly, please consider sending it to me. I have been able to test decoding on around 50 sample files from two different EM38-MKII devices, but could always use more. The device and its logger have a large number of possible setting combinations, and I have not seen all of them. Additionally, a third-party GPS needs to be attached to the device and its data-logger, and GPS output data are notoriously variable by brand and model. Usage ----- @@ -23,22 +23,35 @@ em38 is accompanied by a demo dataset: ``` r library(em38) + +# all-in-one wrapper function: +em38_sf <- n38_to_points(path = system.file("extdata", "em38_demo.n38", package = "em38"), + hdop_filter = 3, out_mode = 'Vertical') + + +# Plot spatialised output (calibrated conductivity for coil separation 0.5m) +plot(em38_sf['cond_05'], pch = 20) +``` + +![](README-example-1.png) + +Its surprisingly difficult to walk in a straight line across a paddock :no\_mouth: + +If you want to look at the intermediate data more closely, + +``` r # import binary file as raw() type matrix n38_mat <- n38_import(system.file("extdata", "em38_demo.n38", package = "em38")) # break matrix into sections according to file spec n38_chunks <- n38_chunk(n38_mat) # decode matrix chunks into useable data n38_decoded <- n38_decode(n38_chunks) - # output an m38 file (comparable to official decode) m38_example <- n38_to_m38(n38_decoded) # write to file as e.g. write(m38_example, paste0('m38_from_R_', Sys.Date(), '.m38')) # spatialise the decoded data -em38_sf <- em38_spatial(n38_decoded, out_mode = 'Vertical') - -# Plot spatialised output (calibrated conductivity for coil separation 0.5m) -plot(em38_sf['cond_05'], pch = 20) +em38_sf <- em38_spatial(n38_decoded, hdop_filter = 3, out_mode = 'Vertical') ``` -![](README-example-1.png) +------------------------------------------------------------------------ diff --git a/data/n38_demo.rda b/data/n38_demo.rda index c970f84..2e627c3 100644 Binary files a/data/n38_demo.rda and b/data/n38_demo.rda differ diff --git a/man/em38_spatial.Rd b/man/em38_spatial.Rd index 0ac8c0d..4e12b76 100644 --- a/man/em38_spatial.Rd +++ b/man/em38_spatial.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/spatialise.R \name{em38_spatial} \alias{em38_spatial} -\title{Spatialise data} +\title{Spatialise EM38 data} \usage{ em38_spatial(n38_decoded = NULL, hdop_filter = 3, out_mode = c("Vertical", "Horizontal")) @@ -24,7 +24,9 @@ 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 must be of survey type 'GPS' and record type 'auto'. +Input n38_decoded object should be of survey type 'GPS' and record type 'auto'. If not, the + function will fail gracefully by returning a list of reasons why the data could not be + converted to points. } \examples{ data('n38_demo') diff --git a/man/n38_import.Rd b/man/n38_import.Rd index 5840f66..06e5dbf 100644 --- a/man/n38_import.Rd +++ b/man/n38_import.Rd @@ -4,11 +4,11 @@ \alias{n38_import} \title{Import EM38 data} \usage{ -n38_import(x = NULL) +n38_import(path = NULL) } \arguments{ -\item{x}{A file path pointing to a valid *.N38 file, produced by a Geonics EM38-MK2 conductivity -sensor connected to an Allegra CX datalogger (and optionally, a GPS device).} +\item{path}{A file path pointing to a valid *.N38 file, produced by a Geonics EM38-MK2 conductivity +sensor connected to an Allegra CX or Archer datalogger (and optionally, a GPS device).} } \value{ A matrix with n rows and 25 columns, containing raw bytes. diff --git a/man/n38_to_points.Rd b/man/n38_to_points.Rd new file mode 100644 index 0000000..7d65a76 --- /dev/null +++ b/man/n38_to_points.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/spatialise.R +\name{n38_to_points} +\alias{n38_to_points} +\title{Import and convert N38 logfiles to points} +\usage{ +n38_to_points(path = NULL, hdop_filter = 3, out_mode = c("Vertical", + "Horizontal")) +} +\arguments{ +\item{path}{A file path pointing to a valid *.N38 file, produced by a Geonics EM38-MK2 +conductivity sensor connected to an Allegra CX or Archer datalogger (and optionally, a GPS +device).} + +\item{hdop_filter}{Numeric, discard GPS data where the Horizontal Dilution of Precision is +greater than this number. Defaults to 3 metres. Set to NULL to keep all readings.} + +\item{out_mode}{Character, em38 dipole mode. Output dataset can only contain Vertical or +Horizontal data, never both.} +} +\value{ +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. +} +\description{ +This is a wrapper function that processes a raw on-disk N38 file into an sf point spatial +dataset. +} +\note{ +Input file should be of survey type 'GPS' and record type 'auto'. If not, the + function will fail gracefully by returning reasons why the data could not be + converted to points. +} +\examples{ +vert_points <- +n38_to_points(path = system.file("extdata", "em38_demo.n38", package = "em38"), + hdop_filter = 3, out_mode = 'Vertical') +}