Introduction

This package provides a wrapper on the OmniDriver from Ocean Insight. The runtime can be downloaded and installed for free. Please, use the Java documentation provided by Ocean Insight. The names of all functions are preserved but change from camel case as in getApiVersion() to a style easier to read and more usual in R. For this example get_api_version().

Instalaltion

Please, see the README file of the package.

Using rOmniDriver

The first step is to load the package, which automatically loads the OminiDriver Java library if it is installed in the computer. We need then to initialize the API by creating a wrapper object in Java. Optionally we can query the API version. Updates to OmniDriver report an earlier API version if the interface has not changed.

Optionally force the printing of a diagnostic message even for a successful loading of the package. We do it here to illustrate the location of the driver and Java.

old.verbose <- options(verbose = TRUE)

We then load the package. It depends on package ‘rJava’ which is also loaded.

## Loading required package: rJava
## now dyn.load("C:/Users/aphalo/AppData/Local/R/win-library/4.4/rJava/libs/x64/rJava.dll") ...
## rOmniDriver:
##   OmniDriver initialization SUCCEEDED
##   OI_HOME: 
## C:\Program Files\Ocean Optics\OmniDriver\OOI_HOME
##   Path: 
## C:\Program Files\Ocean Optics\OmniDriver\OOI_HOME/OmniDriver.jar
options(old.verbose)
# Evaluation of chunks is disabled if package initialization has failed
# Java and/or OmniDriver installations not found or incompatible 
eval_switch <- !getOption("rOmniDriver.offline", TRUE)
opts_chunk$set(eval=eval_switch)
## [1] "2.72"

The next step is to open connections to all Ocean Insight spectrometers directly connected through USB to the local computer. This function does not ‘see’ spectrometers connected through a LAN. It returns the number of spectrometers found. The spectrometers are accessed through and index (integer number) in the range 0 to \(n - 1\) where \(n\) is the number of connected spectrometers.

## [1] 0
# if no spectrometer is attached, evaluation of later chunks is disabled
eval_switch <- eval_switch && n
opts_chunk$set(eval=eval_switch)

In what follows we will explicitly access the first spectrometer, with index equal 0. We use 0L to indicate that we mean a long integer.

get_name(jwrapper = w, sr.index = 0L)
get_serial_number(jwrapper = w, sr.index = 0L)

As the default for sr.index is 0L and arguments can be recognized by position, the code in the next chunk is equivalent to the previous one.

We can query for a description of the optical bench. In this case after obtaining the object describing the bench we directly use the Java functions defined in OmniDriver Java code. We use $ to mean ‘call the Java method’ of this name. As this are the Java methods, their names use camel case.

bench <- get_bench(jwrapper = w, sr.index = 0L)
if (class(bench)[1] == "jobjRef") {
  # Function removed from OmniDriver >= 2.70
  print(bench$getGrating())
  print(bench$getFilterWavelength())
  print(bench$getSlitSize())
}
n_ch <- get_number_of_channels(jwrapper = w, sr.index = 0L)
n_ch

We can also query about other characteristics of the spectrometer, including the limits of the range of accepted integration times.

get_maximum_integration_time(jwrapper = w, sr.index = 0L)
get_minimum_integration_time(jwrapper = w, sr.index = 0L)
get_maximum_intensity(jwrapper = w, sr.index = 0L)
wl <- get_wavelengths(jwrapper = w, sr.index = 0L, ch.index = 0L)
head(wl)
get_firmware_model(jwrapper = w, sr.index = 0L) # API >= 2.40 only
get_firmware_version(jwrapper = w, sr.index = 0L)
get_number_of_pixels(jwrapper = w, sr.index = 0L, ch.index = 0L)
get_number_of_dark_pixels(jwrapper = w, sr.index = 0L, ch.index = 0L)

Some features are available only in some models of spectrometers. Before using these features it is necessary to check whether they are available or not.

The first example is a temperature sensor on the controller board. In this case there is only one method available, which returns the board temperature in degrees centigrades.

if (is_feature_supported_board_temperature(jwrapper = w, sr.index = 0L)) {
  controller.board <- 
    get_feature_controller_board_temperature(jwrapper = w, sr.index = 0L)
  controller.board$getBoardTemperatureCelsius()
} else {
  print("feature not supported")
}

The next example, is for a similar temperature sensor in the CCD detector board.

if (is_feature_supported_detector_temperature(jwrapper = w, sr.index = 0L)) {
  detector.board <- 
    get_feature_detector_board_temperature(jwrapper = w, sr.index = 0L)
  detector.board$getDetectorTemperatureCelsius()
} else {
  print("feature not supported")
}

Another feature is switching between ‘normal’ and ‘raw’ spectral data. Again we have a single method, but in this case accepting an argument.

spectemtrum.type.flag <- 
  is_feature_supported_spectrum_type(jwrapper = w, sr.index = 0L)
if (is.na(spectemtrum.type.flag)) { 
  print("feature no supported by driver")
  } else if (spectemtrum.type.flag) {
  spectrum.type <- get_feature_spectrum_type(jwrapper = w, sr.index = 0L)
  spectrum.type$setSpectrumType(1L) # raw
  spectrum.type$setSpectrumType(0L) # normal
} else {
  print("feature not supported by spectrometer")
}

The integration is set in microseconds. At least in some spectrometer types, out-of-range values are silently ignored. Use the functions described above to find the valid range, or use get_integration_time() to confirm that the set method has succeeded.

set_integration_time(jwrapper = w, time.usec = 1e6, sr.index = 0L, ch.index = 0L)
get_integration_time(jwrapper = w, sr.index = 0L, ch.index = 0L)

Boxcar smoothing is a running averaging along the pixels (wavelengths). It is possible to set the number of pixels as a half width. (Using boxcar smoothing reduces the noise reducing the effective wavelength resolution but maintains the temporal resolution.)

set_boxcar_width(w, half.width.px = 0L, sr.index = 0L, ch.index = 0L)
get_boxcar_width(jwrapper = w, sr.index = 0L, ch.index = 0L)

Setting the number of scans to average to a number larger than one results in the pixel-by-pixel averaging of several scans. (Using scan averaging reduces the temporal resolution but maintains the maximum wavelength resolution.)

set_scans_to_avg(jwrapper = w, n.scans = 4L, sr.index = 0L, ch.index = 0L)
get_scans_to_avg(jwrapper = w, sr.index = 0L, ch.index = 0L)

The correction for detector non-linearity can be applied in the spectrometer, but this can also be disabled. (According to Ocean Insight’ literature the in-spectrometer correction is applied based on the mean counts across the whole array, which seems an odd thing to do. This needs checking as it could distort the shape of the spectrum compared to applying the correction pixel by pixel.)

set_correct_for_detector_nonlinearity(jwrapper = w, correct = 0L, sr.index = 0L, ch.index = 0L)
get_correct_for_detector_nonlineary(jwrapper = w, sr.index = 0L, ch.index = 0L)

This is another correction that can be applied in the spectrometer, based on the reading of a small set of array pixels which are always darkened.

set_correct_for_electrical_dark(jwrapper = w, enable = 0L, sr.index = 0L)
get_correct_for_electrical_dark(jwrapper = w, sr.index = 0L)

The time out is needed only with external triggering, and is the maximum time to wait for a trigger before unblocking the communication with the computer (the spectrometer becoming responsive to new commands).

set_timeout(jwrapper = w, time.millisec = 1000L, sr.index = 0L)

USB time out can be also sometimes needed in with other commands that may not return. This seems to have replaced set timeout in the current version of the API at least for some spectrometers.

set_USB_timeout(jwrapper = w, time.millisec = 2000L, sr.index = 0L)

Finally we acquire a spectrum! Subsequently we query whether it is valid, and whether it is saturated (the sensor is clipping the peaks).

raw.spectrum <- get_spectrum(jwrapper = w, sr.index = 0L, ch.index = 0L)
head(raw.spectrum)
summary(raw.spectrum)
is_spectrum_valid(jwrapper = w, sr.index = 0L, ch.index = 0L)
is_saturated(jwrapper = w, sr.index = 0L, ch.index = 0L)

High speed acquisition

High resolution time stamps

oo_timestamp_now <- high_res_time_stamp(jwrapper = w)
highSpdAcq_allocate_buffer(jwrapper = w, number.of.spectra = 10L)
counts <- highSpdAcq_get_spectrum(jwrapper = w, spectrum.number = 1L)
head(counts)
acq.time1 <- highSpdAcq_get_time_stamp(jwrapper = w, spectrum.number = 1L)
acq.time2 <- highSpdAcq_get_time_stamp(jwrapper = w, spectrum.number = 2L)

print(acq.time1)
print(acq.time2)
get_seconds_time_delta(jwrapper = w, acq.time1, acq.time2)

The last step is always to close the connection to the spectrometers.