# BANTER DCL / BW Functions
library(RSQLite)
library(dplyr)
library(stringr)
library(PamBinaries)
library(seewave)
library(banter)
library(rfPermute)
library(gam)
library(data.table)
library(ggplot2)
############################################################
###### YOU MIGHT ADD TO THESE FUNCTIONS
############################################################
# Binary Processing Functions ---------------------------------------------
# These calculate numbers from the binaries to use as predictors.
# These just read basic information from the binary file, no real processing.
binBasic <- function(bin, ...) {
df <- data.frame(type=bin$type, duration = bin$duration / bin$sampleRate)
for(a in bin$angles) {
df <- bind_cols(df, data.frame(angle=a))
}
names(df) <- c('type', 'duration', paste0('angle', seq_along(bin$angles)))
df
}
# Any calculations that operate on the spectrum should go here.
# calFun should be from pascalCalFun, a list of length channel with gam in each spot
binSpec <- function(bin, highpass=10, calFun, new=TRUE, fast=TRUE) {
result <- list()
for(chan in 1:ncol(bin$wave)) {
thisWave <- bin$wave[,chan]
sr <- bin$sampleRate
thisWave <- seewave::bwfilter(thisWave, f=sr, n=4, from=highpass*1e3, output='sample')
########### TEMP FOR BBW #############
if(TRUE) {
# fftSize <- switch(as.character(sr),
# '288000' = 736,
# '256000' = 654,
# '192000' = 456,
# '96000' = 244) # 2.5ms supposed from risso.geographic jasa17
# 2.5ms window, round to even number
fftSize <- round(sr * .0025, 0)
fftSize <- fftSize + (fftSize %% 2)
# Shortening click wave by getting numpoints=fftsize around peak of wav. Length fft+1
wavPeak <- which.max(thisWave)
tryCatch({
if(length(thisWave) <= fftSize) {
# do nothing
} else if(wavPeak > fftSize/2) {
if((wavPeak + fftSize/2) <= length(thisWave)) {
thisWave <- thisWave[(wavPeak - fftSize/2):(wavPeak + fftSize/2)]
} else {
thisWave <- thisWave[(length(thisWave)-fftSize):length(thisWave)]
}
} else {
thisWave <- thisWave[1:(fftSize+1)]
}
}, error = function(e) {
browser()
})
}
# Calculate the spectrum
thisSpec <- spec(thisWave, f=sr, wl=512, plot=FALSE, norm=FALSE)
# Remove frequency below highpass
# thisSpec <- thisSpec[thisSpec[,1]>highpass,]
# Do any calculations you want. Here just getting peak frequency.
peak <- thisSpec[which.max(thisSpec[,2]), 1]*1e3
thisDf <- data.frame(peak=peak)
if(new) {
# TKEO - skip 1st .001s to avoid startup artifacts, energy is 2nd col
thisTk <- TKEO(thisWave, f=sr, M=1,plot=F)
# THIS DOESNT WORK REALLY SHORT CLICKS
tkEnergy <- thisTk[(.001*sr):length(thisWave),2]
tkDb <- 10*log10(tkEnergy-min(tkEnergy, na.rm=TRUE))
tkDb <- tkDb - max(tkDb, na.rm=TRUE)
tkDb[!is.finite(tkDb)] <- NA
noiseLevel <- median(tkDb, na.rm=TRUE)
if(is.na(noiseLevel)) {
noiseLevel <- 0
}
noiseThresh <- quantile(thisTk[,2], probs=.4, na.rm=TRUE)*100
dur <- subset(thisTk, thisTk[,2] >= noiseThresh)
if(length(dur)==0) {
dur <- 0
} else {
dur <- 1e6*(max(dur[,1])-min(dur[,1]))
}
thisSpec <- spec(thisWave, f=sr, wl=fftSize, norm=FALSE, correction='amplitude', plot=FALSE)
relDb <- 20*log10(thisSpec[,2])
relDb[!is.finite(relDb)] <- NA
# Calibration
newClick <- data.frame(Freq=thisSpec[,1]*1e3, Sens = relDb)
if(!missing(calFun)) {
#DO CAL
predValue <- predict.Gam(calFun[[chan]], newdata=newClick)
predValue <- predValue - predValue[1]
clickSens <- relDb-predValue
clickSens <- clickSens - max(clickSens)
# browser()
} else {
# if no cal, just use original relDb
clickSens <- relDb - max(relDb)
}
calibratedClick <- cbind(newClick$Freq/1e3, clickSens)
thisDf <- peakTrough(calibratedClick)
# thisDf <- peakTrough(calibratedClick, plot=runif(1)<5, title=paste(bin$UID, bin$date))
if(fast) {
Qfun <- Qfast
} else Qfun <- seewave::Q
dbBW10 <- data.frame(Qfun(calibratedClick, f=sr, level=-10, plot=FALSE))
colnames(dbBW10) <- c('Q_10dB', 'PeakHz_10dB', 'fmin_10dB', 'fmax_10dB', 'BW_10dB')
dbBW10$centerHz_10dB <- dbBW10$fmax_10dB - (dbBW10$BW_10dB/2)
dbBW3 <- data.frame(Qfun(calibratedClick, f=sr, level=-3, plot=FALSE))
colnames(dbBW3) <- c('Q_3dB', 'PeakHz_3dB', 'fmin_3dB', 'fmax_3dB', 'BW_3dB')
dbBW3$centerHz_3dB <- dbBW3$fmax_3dB - (dbBW3$BW_3dB/2)
thisDf <- thisDf %>%
mutate(duration = dur, noiseLevel = noiseLevel) %>%
cbind(dbBW10, dbBW3)
}
# If you wanted to add more, just calculate it and add it as another column in this data frame
# Ex: thisDf$fakeNumber <- 42
# names(thisDf) <- paste0('chan', chan, names(thisDf))
thisDf$Channel <- chan
result[[chan]] <- thisDf
}
result <- bind_rows(result)
result$UID <- bin$UID
result
}
############################################################
### YOU PROBABLY DONT WANT TO CHANGE ANYTHING BELOW HERE ###
############################################################
# Read DBs in folder ------------------------------------------------------
detFromDb <- function(dir='./devel/BW' , binDir, binFuns = list(), binList = NULL, skip=0) {
#### Function input checking
types <- c('WhistlesMoans', 'ClickDetector', 'Rocca')
if(class(binFuns) != 'list') {
stop('binFuns must be a list with names ', paste(types, collapse=', '))
}
for(type in types) {
if(!(type %in% names(binFuns))) {
cat('No functions provided for ', type, '\n')
binFuns[[type]] <- list()
}
}
# Warn if some supplied binFuns arent functions
binFuns <- lapply(binFuns, function(x) {
if(length(x)==0) {
return(x)
} else {
check <- sapply(x, is.function)
if(any(!check)) {
cat('Some items in "binFuns" argument are not functions. \n')
}
x[check]
}
})
#### End function input checking
if(missing(binDir)) {
binDir <- paste0(dir, '/Binaries')
}
dbList <- tools::list_files_with_exts(dir, exts='sqlite3')
cat('Getting binary list... (this may take a while)\n')
if(is.null(binList)) {
binList <- list.files(binDir, pattern='pgdf', recursive=TRUE, full.names=TRUE)
saveRDS(binList, file='binaryList.RData')
cat('Saving list of binaries to "binaryList.RData" \n If you re-run this batch without changing any of the',
'binary files then add the argument binList=readRDS("binaryList.RData") to save some time.')
}
cat('Loading databases... \n')
pb <- txtProgressBar(min=0, max=length(dbList), style=3)
unlist(lapply(seq_along(dbList), function(dbfile) {
setTxtProgressBar(pb, dbfile)
if(dbfile/length(dbList) >= skip) {
############ LOAD DB FROM HERE #############
con <- dbConnect(drv=SQLite(), dbList[dbfile])
detList <- getDetectorList(con)
detTables <- purrr::map2(detList$Module_Name, detList$Module_Type, function(module, name) {
tryCatch({
data <- switch(module,
WhistlesMoans = wmReader(con, name, binFuns$WhistlesMoans, binList),
ClickDetector = cdReader(con, name, binFuns$ClickDetector, binList),
Rocca = roccaReader(con, name, binFuns$Rocca, binList)) %>%
mutate(db = basename(dbList[dbfile]))
}, error = function(e) {
cat('\nError in db', dbList[dbfile], paste(c('Module: ', 'Name: '), c(module, name)), '\n')
print(e)
# stop(e)
NULL
})
})
names(detTables) <- detList$Module_Type
dbDisconnect(con)
whichNotNull <- sapply(detTables, function(x) !is.null(x))
detTables[whichNotNull]
}
}
), recursive = FALSE)
}
# Get right tables --------------------------------------------------------
getDetectorList <- function(con) {
mods <- dbReadTable(con, 'PamguardModules')
detTables <- mods %>%
mutate(Module_Name=str_trim(Module_Name),
Module_Name=gsub(' ', '', Module_Name),
Module_Type=str_trim(Module_Type),
Module_Type=gsub(' ', '_', Module_Type)) %>%
filter(Module_Name %in% c('ClickDetector', 'WhistlesMoans', 'Rocca')) %>%
distinct(Module_Type, Module_Name)
detTables
}
# Call type readers -------------------------------------------------------
wmReader <- function(con, name, binFuns, binList) {
contours <- dbReadTable(con, paste0(name,'_Localised_', name, '_Contours'))
whistles <- dbReadTable(con, name)
# If no supplied functions just return this
if(length(binFuns)==0) {
whistles <- mutate(whistles, UTC = as.POSIXct(UTC))
return(whistles)
} else {
# DO STUFF
}
}
cdReader <- function(con, name, binFuns, binList) {
clicks <- dbReadTable(con, paste0(name, '_Clicks'))
events <- dbReadTable(con, paste0(name, '_OfflineEvents')) %>%
mutate(eventType = str_trim(eventType)) %>%
select(Id, eventType)
offClicks <- dbReadTable(con, paste0(name, '_OfflineClicks')) %>%
mutate(BinaryFile = str_trim(BinaryFile),
UTC = as.POSIXct(as.character(UTC), format='%Y-%m-%d %H:%M:%OS', tz='UTC')) %>%
select(Id, UTC, EventId, BinaryFile, ClickNo) %>%
filter(EventId %in% events$Id) %>%
left_join(events, by = c('EventId' = 'Id')) %>%
mutate(EventId = paste0(EventId, '_', basename(con@dbname)))
offClicks <- bind_rows(lapply(split(offClicks, offClicks$EventId), function(ev) {
ev <- arrange(ev, UTC)
if(nrow(ev)==1) {
ev$ICI <- 0
} else {
nextTime <- c(ev$UTC[2:nrow(ev)], ev$UTC[nrow(ev)])
ev$ICI <- as.numeric(nextTime - ev$UTC)
}
ev
})) %>% data.table() %>% setkey(UTC)
soundAcquisition <- dbReadTable(con, 'Sound_Acquisition') %>%
mutate(UTC = as.POSIXct(as.character(UTC), format='%Y-%m-%d %H:%M:%OS', tz='UTC'),
Status=str_trim(Status)) %>%
filter(Status=='Start') %>%
arrange(UTC) %>%
select(UTC, sampleRate) %>%
data.table() %>% setkey(UTC)
# This rolling join rolls to the first time before. Since we filtered to only starts, it goes back
# to whatever the last Start was.
offClicks <- soundAcquisition[offClicks, roll = TRUE] %>% data.frame()
# If no supplied functions just return this
if(length(binFuns)==0) {
return(offClicks)
} else {
bind_rows(lapply(split(offClicks, offClicks$BinaryFile), function(db) {
db <- arrange(db, ClickNo)
thisBinFile <- grep(db$BinaryFile[1], binList, value=TRUE)
# Checking if we have right binary file
if(length(thisBinFile) == 0) {
warning(db$BinaryFile[1], ' not found in list')
NULL
} else {
if(length(thisBinFile) > 1) {
for(file in thisBinFile) {
thisBin <- loadPamguardBinaryFile(file)$data # make faster by changing load to pick specific numbers
# If binary doesnt have enough clicks, not a match
if(length(thisBin) < max(db$ClickNo) + 1) {
next
}
# If UIDS match or first times match, this is the right file
if(('UID' %in% names(db)) &
('UID' %in% names(thisBin[[1]]))) {
if(identical(db$UID[1], thisBin[[db$ClickNo[1]+1]]$UID)) {
break
} else next
} else {
if(identical(db$UTC[1], thisBin[[db$ClickNo[1]+1]]$date)) {
break
} else next
}
cat('Couldnt find match for', db$BinaryFile[1])
}
} else { # case when exactly 1 match
thisBin <- loadPamguardBinaryFile(thisBinFile)$data
}
if(max(db$ClickNo + 1) > length(thisBin)) {
warning('Looks like wrong binary file loaded, ClickNo does not exist in binary. \n',
'Problem in ', thisBinFile)
db <- filter(db, ClickNo + 1 <= length(thisBin))
}
# Adding information to binary needed for functions
thisBin <- thisBin[db$ClickNo+1]
for(i in seq_along(thisBin)) {
thisBin[[i]]$sampleRate <- db$sampleRate[i]
}
############ TEMP FOR BBW ############
if(TRUE) {
calFun <- pascalCalFun(basename(con@dbname))
}
# apply all functions to binaries and append to database data
cbind(db,
bind_cols(lapply(binFuns, function(f) {
bind_rows(lapply(thisBin, function(row) {
f(row, calFun=calFun) ############ THIS IS MAYBE TEMP SOLUTION JUST FOR NOW #############
}))
}))
)
}
}))
}
}
roccaReader <- function(con, name, binFuns, binList) {
encounter <- dbReadTable(con, paste0(name, '_Encounter_Stats'))
whistles <- dbReadTable(con, paste0(name, '_Whistle_Stats'))
# If no supplied functions just return this
if(length(binFuns)==0) {
whistles <- mutate(whistles, UTC = as.POSIXct(UTC))
return(whistles)
} else {
# DO STUFF
}
}
# PASCAL Splitting --------------------------------------------------------
# Take output from detFromDb and split into detectors based on the type col
# JUST SPLIT ONE DATABASE AT A TIME THEN EXPORT TO EVENT
pascalTypeSplit <- function(detectorList, freqBins=NULL) {
detectorList <- detectorList[which(sapply(detectorList, nrow) != 0)]
if(is.null(freqBins)) {
squishList(
unlist(lapply(detectorList, function(x) split(x, x$type)), recursive=FALSE)
)
} else {
squishList(
unlist(lapply(detectorList, function(x) {
x$type <- cut(x[['chan1peak']], breaks = freqBins, labels=FALSE)
split(x, x$type)
}), recursive=FALSE)
)
}
}
# Pascal calibration selection
pascalCalFun <- function(stationName) {
stationName <- gsub('(Station-[0-9\\-]*)_.*$', '\\1', stationName)
HTI92 <- paste0('Station-', c(1, 3, 5, 6, 8, 9, 10, 12, 15, 16, 19, 22, 23, 24, 25,'26-29', '27-30'))
if(stationName %in% HTI92) {
HP92Cal <- read.csv('./devel/BW/Frequency response of HTI-92 hydrophone from SM3M manual.csv')
gam92 <- gam(Sensitivity~s(Freq, 50), data=HP92Cal)
HP96Cal <- read.csv('./devel/BW/Frequency response of HTI-96min hydrophone from SM3M manual.csv')
gam96 <- gam(Sensitivity~s(Freq, 50), data=HP96Cal)
list(ch0=gam92, ch1=gam96)
} else {
HP96Cal <- read.csv('./devel/BW/Frequency response of HTI-96min hydrophone from SM3M manual.csv')
gam96 <- gam(Sensitivity~s(Freq, 50), data=HP96Cal)
list(ch0=gam96, ch1=gam96)
}
}
# Combine things with same name in a list. Does no checking, so dont fuck up
squishList <- function(list) {
myNames <- unique(names(list))
result <- vector('list', length=length(myNames))
names(result) <- myNames
for(n in myNames) {
whichThisName <- which(names(list)==n)
result[[n]] <- bind_rows(list[whichThisName])
}
result
}
bbw_data <- function(dir='./devel/BW', binFuns = list(ClickDetector=list(binBasic, binSpec)), binList=NULL, skip=0, ...) {
detectorData <- detFromDb(dir=dir, binFuns=binFuns, binList=binList, skip=skip) %>%
pascalTypeSplit(...)
specCount <- sapply(detectorData, function(x) length(unique(x$eventType)))
singleSpec <- which(specCount<=1)
if(length(singleSpec) > 0) {
warning('Detectors ', names(specCount)[singleSpec], ' have 1 or less species. Removing them.')
detectorData[-singleSpec]
} else {
detectorData
}
}
bbw_exportDetections <- function(bbwData) {
for(detector in seq_along(bbwData)) {
bbwData[[detector]] <- bbwData[[detector]] %>%
mutate(Id = Id + detector * 1e6) %>%
rename(event.id=EventId, call.id=Id) %>%
select(-UTC, -BinaryFile, -ClickNo, -eventType, -type, -db)
}
bbwData
}
bbw_exportEvents <- function(bbwData) {
bind_rows(lapply(bbwData, function(x) {
distinct(x, eventType, EventId) %>%
rename(species=eventType, event.id=EventId)
})) %>% distinct()
}
bbw_makeModel <- function(bbwData) {
initBanterModel(bbw_exportEvents(bbwData)) %>%
addBanterDetector(bbw_exportDetections(bbwData), ntree=10, sampsize=1) %>%
runBanterModel(ntree=100, sampsize=1)
}
# New Q From Seewave ------------------------------------------------------
Qfast <- function(spec = calibratedClick,
f = testBin$sampleRate,
level = -10,
mel = FALSE,
plot = FALSE,
colval = "red",
cexval = 1,
fontval = 1,
flab = NULL,
alab = "Relative amplitude (dB)",
type = "l", ...) {
if (!is.null(f) & mel) {
f <- 2 * mel(f/2)
}
if (is.null(f)) {
if (is.vector(spec))
stop("'f' is missing")
else if (is.matrix(spec))
f <- spec[nrow(spec), 1] * 2000 * nrow(spec)/(nrow(spec) - 1)
}
if (is.matrix(spec)) {
range <- c(spec[1,1], spec[nrow(spec),1]) ### THERES NO RANGE IF SPEC IS VEC
# spectest <- spec
spec <- spec[, 2]
}
specMax <- which.max(spec)
if(length(specMax)==0) {
return(list(Q = 0, dfreq = 0, fmin = 0, fmax = 0, bdw = 0))
}
if (spec[specMax] == 1)
stop("data must be in dB")
if (specMax == 1)
stop("maximal peak cannot be the first value of the spectrum")
n1 <- length(spec)
level2 <- spec[specMax] + level
f0 <- specMax
f0khz <- (((f0-1)/(n1-1)))*(range[2]-range[1]) + range[1] # These and others below need -1s to properly scale
specA <- spec[1:f0]
specB <- spec[f0:length(spec)]
negA <- which(specA <= level2)
if(length(negA) == 0) {
fA <- 1
} else {
firstNegA <- max(which(specA <= level2)) # btwn this and next
fA <- approx(x=spec[firstNegA:(firstNegA+1)], y=firstNegA:(firstNegA+1), xout=level2)$y
}
fAkhz <- ((fA-1)/(n1-1)) * (range[2]-range[1]) + range[1]
nA <- length(specA)
negB <- which(specB <= level2)
if(length(negB) == 0) {
fB <- length(spec)
} else {
firstNegB <- min(negB) + (nA - 1) # btwn this and prev
fB <- approx(x=spec[(firstNegB-1):firstNegB], y=(firstNegB-1):firstNegB, xout=level2)$y
}
fBkhz <- ((fB-1)/(n1-1)) * (range[2]-range[1]) + range[1]
Q <- f0khz/(fBkhz-fAkhz)
results <- list(Q = Q, dfreq = f0khz, fmin = fAkhz, fmax = fBkhz,
bdw = fBkhz - fAkhz)
results <- lapply(results, function(x) ifelse(length(x)==0, 0, x)) # Temp fix on missing
if (plot) {
if (is.null(flab)) {
if (mel)
flab <- "Frequency (kmel)"
else flab <- "Frequency (kHz)"
}
x <- seq(range[1], range[2], length.out = n1)
plot(x = x, y = spec, xlab = flab, ylab = alab, type = type,
...)
arrows(fAkhz, level2, fBkhz, level2, length = 0.1, col = colval,
code = 3, angle = 15)
text(paste("Q =", as.character(round(Q, 2))), x = fBkhz,
y = level2, pos = 4, col = colval, cex = cexval,
font = fontval)
invisible(results)
}
return(results)
}
# Helpers -----------------------------------------------------------------
# Running average
runAvg <- function(x, l=5) {
if(l > length(x)) {
rep(mean(x, na.rm=TRUE), length(x))
} else {
sapply(seq_along(x), function(s) {
if(s <= (l-1)/2) {
mean(x[1:(s+(l-1)/2)], na.rm=TRUE)
} else if((s+(l-1)/2) > length(x)) {
mean(x[(s-(l-1)/2):length(x)], na.rm=TRUE)
} else {
mean(x[(s-(l-1)/2):(s+(l-1)/2)], na.rm=TRUE)
}
})
}
}
# Get second peak and trough between. freqBounds is (minDist, maxDist)
# If nothing in this bound, return 0 for 2nd peak and trough
peakTrough <- function(spec, freqBounds=c(10, 30), dBMin=-15, plot=FALSE, avg=5, title='hello', export=FALSE) {
# Default values to return if we dont find other peaks
peak2 <- 0; peak2dB <- dBMin
trough <- 0; troughdB <- dBMin
peak3 <- 0; peak3dB <- dBMin
trough2 <- 0; trough2dB <- dBMin
specDf <- data.frame(Freq=spec[,1], dB=runAvg(spec[,2], l=avg)) # Smooth with local average, nearest 5
wherePeak <- which.max(specDf$dB)
peak <- specDf$Freq[wherePeak]
peakdB <- specDf$dB[wherePeak]
if(length(peak)==0) { # Not sure how this would happen, just in case
peak <- 0; peakdB <- dBMin
}
peak2Df <- specDf %>%
mutate(before = c(specDf$dB[1], specDf$dB[1:(nrow(specDf)-1)]),
after = c(specDf$dB[2:nrow(specDf)], specDf$dB[nrow(specDf)]),
isPeak = (dB > before) & (dB >= after)) %>%
filter(isPeak,
((Freq >= (peak + freqBounds[1])) & (Freq <= (peak + freqBounds[2]))) |
((Freq <= (peak - freqBounds[1])) & (Freq >= (peak - freqBounds[2]))),
Freq != peak,
dB >= dBMin)
if(nrow(peak2Df) > 0) {
wherePeak2 <- which.max(peak2Df$dB)
peak2 <- peak2Df$Freq[wherePeak2]
peak2dB <- peak2Df$dB[wherePeak2]
peak3Df <- peak2Df %>%
filter((Freq >= (peak2 + freqBounds[1])) |
(Freq <= (peak2 - freqBounds[1])),
Freq != peak2)
if(nrow(peak3Df) > 0) {
wherePeak3 <- which.max(peak3Df$dB)
peak3 <- peak3Df$Freq[wherePeak3]
peak3dB <- peak3Df$dB[wherePeak3]
}
}
allPeaks <- sort(c(peak, peak2, peak3))
allPeaks <- allPeaks[allPeaks != 0]
# Find troughs based on num of non-zero peaks. Change nothing if only 1.
if(length(allPeaks)==2) {
troughDf <- filter(specDf, Freq > allPeaks[1], Freq < allPeaks[2])
whereTrough <- which.min(troughDf$dB)
trough <- troughDf$Freq[whereTrough]
troughdB <- troughDf$dB[whereTrough]
} else if(length(allPeaks)==3) {
troughDf <- filter(specDf, Freq > allPeaks[1], Freq < allPeaks[2])
whereFirst <- which.min(troughDf$dB)
first <- troughDf$Freq[whereFirst]
firstdB <- troughDf$dB[whereFirst]
troughDf <- filter(specDf, Freq > allPeaks[2], Freq < allPeaks[3])
whereSecond <- which.min(troughDf$dB)
second <- troughDf$Freq[whereSecond]
seconddB <- troughDf$dB[whereSecond]
# Want lowest trough to be labelled "trough", not "trough2"
if(firstdB <= seconddB) {
trough <- first
troughdB <- firstdB
trough2 <- second
trough2dB <- seconddB
} else {
trough <- second
troughdB <- seconddB
trough2 <- first
trough2dB <- firstdB
}
}
peakToPeak2 <- abs(peak-peak2)
peakToPeak3 <- abs(peak-peak3)
peak2ToPeak3 <- abs(peak2-peak3)
if(plot) {
# I DONT THINK THIS WORKS WITH RECTS - CHECK LOGIC LATER
freqLines <- sort(peak + c(freqBounds, -1*freqBounds))
graphDf <- data.frame(Freq = c(peak, peak2, peak3, trough, trough2),
dB = c(max(specDf$dB), peak2dB, peak3dB, troughdB, trough2dB),
Type = c('Highest Peak', 'Second Peak', 'Third Peak', 'Trough / Notch', 'Trough / Notch'))
g <- ggplot() + geom_line(data=specDf, aes(x=Freq, y=dB)) +
geom_vline(xintercept=freqLines, color='goldenrod1') +
geom_hline(yintercept=dBMin, color='blue') +
geom_point(data=graphDf, aes(x=Freq, y=dB, color=Type), size=3) +
coord_cartesian(xlim=range(specDf$Freq), ylim=range(specDf$dB)) +
scale_x_continuous(breaks=seq(0,140,20)) +
labs(title='Finding Peaks and Troughs', x='Frequency', y='Relative dB') +
## FIX ME
geom_rect(aes(xmin=c(freqLines[1], freqLines[3]), xmax=c(freqLines[2], freqLines[4]), ymin=dBMin, ymax=0, fill='a'), alpha=.1) +
geom_rect(aes(xmin=c(-5, freqLines[2], freqLines[4]), xmax=c(freqLines[1], freqLines[3], 150), ymin=dBMin, ymax=0, fill='b'), alpha=.15) +
geom_rect(aes(xmin=c(freqLines[1], freqLines[3]), xmax=c(freqLines[2], freqLines[4]), ymin=-130, ymax=dBMin, fill='c'), alpha=.1) +
### END FIX
scale_fill_manual(values=c('green', 'blue', 'yellow'), labels=c('Range to Search', 'dB Range', 'Frequency Range'), name='') +
theme(plot.title=element_text(hjust=.5))
suppressWarnings(print(g))
if(export) {
return(g)
}
}
data.frame(peak = peak, peak2 = peak2, peak3 = peak3,
trough = trough, trough2 = trough2,
peakToPeak2 = peakToPeak2, peakToPeak3 = peakToPeak3, peak2ToPeak3 = peak2ToPeak3)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.