This function isolates detections that occurred at `effectively the same time' at different receivers with overlapping or non-overlapping detection containers. To implement the function, a dataframe of acoustic detections for a specific individual (acoustics) is required. Within this dataframe, the function isolates any detections that occurred within a user-specified time interval (clock_drift) at different receivers. If a list of the receivers with overlapping detection containers in space and time is supplied (overlaps), the function also flags the subset of these detections that occurred at receivers with overlapping or non-overlapping detection containers. This information is important for identifying potential false detections and the implementation of the AC* algorithm(s) (see Details). The function returns this information via summary message(s) along with a dataframe of the overlapping detections.

get_detection_overlaps(acoustics, overlaps = NULL, clock_drift = 5)

Arguments

acoustics

A dataframe of passive acoustic telemetry detection time series (see dat_acoustics for an example) for a single individual. This must contain an integer vector of receiver IDs, named `receiver_id' and a POSIXct vector of time stamps when detections were made, named `timestamp'.

overlaps

(optional) A named list, from get_detection_containers_overlap, that defines, for each receiver, for each day over its deployment period, whether or not its detection container overlapped with those of other receivers.

clock_drift

A number that defines the time (s) between sequential detections at which they are considered to have occurred at `effectively the same time'.

Value

The function returns a message that defines the number of detections at different receivers that occurred at effectively the same time (within clock_drift) and, if overlaps is supplied, the subset of these that occurred at non-overlapping receivers. A dataframe is also invisibly returned that records the details of overlapping detections. This includes the following columns:

  • timestamp_1, a POSIXct vector of time stamps at which detections were made;

  • receiver_id_1, an integer identifier of the receivers at which detections were made;

  • timestamp_2, a POSIXct vector of the time stamps at which immediately subsequent detections were made;

  • receiver_id_2, an integer identifier of the receivers at which the immediately subsequent detections were made;

  • diff_time, a numeric vector that defines the time (s) between consecutive detections (timestamp_1 and timestamp_2);

  • detection_in_overlapping_container, a binary vector that defines whether (1) or not (0) the detection containers of receiver_id_1 and receiver_id_2 overlapped at the time of the detection (this is only included if overlaps is provided);

Details

Detections at different receivers that occur at effectively the same time have important implications for inferences of animal movement patterns, especially via the AC* algorithm(s) in flapper (e.g. acdc). Within the AC* algorithm(s), when an individual is detected at the same time at two different receivers, detection probability kernels dictate that these receivers must have overlapping detection containers. If the detection containers do not overlap, this suggests that either (a) receiver clocks are not well-aligned; (b) the definition of `effectively the same time' is be overly large (such that the individual could move from within the detection container of one receiver into the detection container of another); (c) detection containers are too small and detection probability is higher than realised; and/or (d) one or more of the detections are false. The most likely cause may be guided by knowledge of the array design, detection probability and false detection algorithms. For example, if it is plausible that detection containers are too small, repeating the implementation of this function with larger containers may indicate whether or not this is likely to have been the case: if so, all detections that occurred at effectively the same time at receivers with non-overlapping detection containers should be captured by the large containers (though this does not rule out other explanations). The purpose of this function is to flag any such detections so that they can be investigated and addressed prior to the implementation of the AC* algorithm(s).

Author

Edward Lavender

Examples

#### Example (1): Implement function using detection data for an example individual
dat_acoustics_25 <- dat_acoustics[dat_acoustics$individual_id == 25, ]
dat <- get_detection_overlaps(acoustics = dat_acoustics_25)
#> 517 observation(s) identified at another receiver within 5 secs.
utils::head(dat)
#>           timestamp_1 receiver_id_1 receiver_id_2         timestamp_2 diff_time
#> 1 2016-03-17 02:56:00            26            21 2016-03-17 02:56:00         0
#> 2 2016-03-17 04:08:00            26            21 2016-03-17 04:08:00         0
#> 3 2016-03-17 05:06:00            26            31 2016-03-17 05:06:00         0
#> 4 2016-03-17 05:10:00            31            26 2016-03-17 05:10:00         0
#> 5 2016-03-17 05:20:00            31            26 2016-03-17 05:20:00         0
#> 6 2016-03-17 08:50:00            26            31 2016-03-17 08:50:00         0

#### Example (2): Implement function, including information on detection containers

## Get detection container overlaps to include in function
## (see ?flapper::get_detection_container_overlaps)
# Define receiver locations
proj_wgs84 <- sp::CRS(SRS_string = "EPSG:4326")
proj_utm <- sp::CRS(SRS_string = "EPSG:32629")
rownames(dat_moorings) <- dat_moorings$receiver_id
xy <- sp::SpatialPoints(
  dat_moorings[, c("receiver_long", "receiver_lat")],
  proj_wgs84
)
xy <- sp::spTransform(xy, proj_utm)
xy@data <- as.data.frame(xy)
rownames(xy@data) <- dat_moorings$receiver_id
# Get receiver-specific detection containers
containers <- get_detection_containers(xy, byid = TRUE)
#> Warning: GEOS support is provided by the sf and terra packages among others

containers_df <- dat_moorings[, c(
  "receiver_id",
  "receiver_start_date",
  "receiver_end_date"
)]
row.names(containers_df) <- dat_moorings$receiver_id
containers <- sp::SpatialPolygonsDataFrame(containers, containers_df)
# Define detection container overlaps
overlaps <- get_detection_containers_overlap(containers = containers)

## Implement function with detection container overlaps included
dat <- get_detection_overlaps(acoustics = dat_acoustics_25, overlaps = overlaps)
#> 517 observation(s) identified at another receiver within 5 secs.
#> Of these, there are 2 observation(s) within 5 secs that are not in overlapping containers.
#> These simulataneous detections at non-overlapping receivers occurred at 15 unique receiver pair(s): 3-9, 12-44, 21-26, 21-57, 26-31, 41-47, 43-57, 45-56, 45-57, 46-54, 46-52, 47-52, 49-53, 50-51, 52-54.
utils::head(dat)
#>           timestamp_1 receiver_id_1 receiver_id_2         timestamp_2 diff_time
#> 1 2016-03-17 02:56:00            26            21 2016-03-17 02:56:00         0
#> 2 2016-03-17 04:08:00            26            21 2016-03-17 04:08:00         0
#> 3 2016-03-17 05:06:00            26            31 2016-03-17 05:06:00         0
#> 4 2016-03-17 05:10:00            31            26 2016-03-17 05:10:00         0
#> 5 2016-03-17 05:20:00            31            26 2016-03-17 05:20:00         0
#> 6 2016-03-17 08:50:00            26            31 2016-03-17 08:50:00         0
#>   detection_in_overlapping_container
#> 1                                  1
#> 2                                  1
#> 3                                  1
#> 4                                  1
#> 5                                  1
#> 6                                  1

#### Implement function across all individuals
# For some individuals, there are simultaneous detections at receivers with
# ... non overlapping detection containers, suggesting these are probably too small.
dat_by_id <-
  lapply(
    split(dat_acoustics, dat_acoustics$individual_id),
    function(acc_for_id) {
      print(paste("individual_id", acc_for_id$individual_id[1], "-------"))
      get_detection_overlaps(acoustics = acc_for_id, overlaps = overlaps)
    }
  )
#> [1] "individual_id 25 -------"
#> 517 observation(s) identified at another receiver within 5 secs.
#> Of these, there are 2 observation(s) within 5 secs that are not in overlapping containers.
#> These simulataneous detections at non-overlapping receivers occurred at 15 unique receiver pair(s): 3-9, 12-44, 21-26, 21-57, 26-31, 41-47, 43-57, 45-56, 45-57, 46-54, 46-52, 47-52, 49-53, 50-51, 52-54.
#> [1] "individual_id 28 -------"
#> 239 observation(s) identified at another receiver within 5 secs.
#> Of these, there are 111 observation(s) within 5 secs that are not in overlapping containers.
#> These simulataneous detections at non-overlapping receivers occurred at 9 unique receiver pair(s): 3-9, 3-33, 21-30, 21-26, 21-57, 43-57, 49-53, 50-51, 45-56.
#> [1] "individual_id 35 -------"
#> 294 observation(s) identified at another receiver within 5 secs.
#> Of these, there are 9 observation(s) within 5 secs that are not in overlapping containers.
#> These simulataneous detections at non-overlapping receivers occurred at 6 unique receiver pair(s): 3-9, 3-33, 21-43, 21-45, 43-57, 45-57.
## Test this hypothesis by re-implementing approach with larger containers
# Re-define containers and receiver overlap
containers <- get_detection_containers(xy, detection_range = 750, byid = TRUE)
#> Warning: GEOS support is provided by the sf and terra packages among others

containers <- sp::SpatialPolygonsDataFrame(containers, containers_df)
overlaps <- get_detection_containers_overlap(containers = containers)
# Re-implement algorithm
dat_by_id <-
  lapply(
    split(dat_acoustics, dat_acoustics$individual_id),
    function(acc_for_id) {
      print(paste("individual_id", acc_for_id$individual_id[1], "-------"))
      get_detection_overlaps(acoustics = acc_for_id, overlaps = overlaps)
    }
  )
#> [1] "individual_id 25 -------"
#> 517 observation(s) identified at another receiver within 5 secs.
#> Of these, there are 0 observation(s) within 5 secs that are not in overlapping containers.
#> [1] "individual_id 28 -------"
#> 239 observation(s) identified at another receiver within 5 secs.
#> Of these, there are 0 observation(s) within 5 secs that are not in overlapping containers.
#> [1] "individual_id 35 -------"
#> 294 observation(s) identified at another receiver within 5 secs.
#> Of these, there are 0 observation(s) within 5 secs that are not in overlapping containers.
# There are now no observations within clock_drift at receivers with
# ... non-overlapping containers.