Persistence of Vision Frisbee

The first time I saw a persistence of vision (POV) clock I thought: “Wouldn’t that be cool on the side of the frisbee?”. It is a fun weekend project to make one. Here is an action shot of the POV frisbee I made:

IMG_4897

It looks better in person – this picture is the best we could do with a Canon camera held on a level surface. The frisbee is spelling out ‘bovine aero’. Below is a picture of a test in a darkened room:

IMG_4863

This is the perfect project for Adafruit’s tiny, lightweight Arduino compatible Trinket. The total cost of the materials was around $30:

Assembly is as simple as hooking up an LED & resistor in series to each digital out pin, writing some code to blink the lights in the correct order (see below), and attaching the circuit the frisbee. Below are some pictures taken during assembly:

IMG_20141013_142138647_HDRIMG_20141013_142156068_HDR

Some insights, if anyone else tries this:

  • I considered using a load sensor to determine the rotation rate of the frisbee (a = v^2/r). If you know the rotational speed of the frisbee, you can time the LED outputs to write more consistently spaced letters. However, it turns out the POV illusion looks OK if you use a fixed letter writing rate (I used an ‘on’ time 1 ms per vertical column of letter).
  • To mount the LEDs on the frisbee, I drilled holes in the side and covered them with electrical tape. Punching the LEDs through the tape provided a stable mount for the LEDS.
  • I used letters made up of 5×5 pixel blocks. A better POV display could use more vertically stacked LEDs, or even multicolored LEDs.

Click below to see the code I used:

int B[25] = {1,1,1,1,1, 1,0,1,0,1, 1,0,1,0,1, 0,1,0,1,0, 0,0,0,0,0};
int O[25] = {0,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,0, 0,0,0,0,0};
int V[25] = {1,1,1,1,0, 0,0,0,1,0, 0,0,0,0,1, 0,0,0,1,0, 1,1,1,1,0};
int I[25] = {0,0,0,0,0, 1,0,0,0,1, 1,1,1,1,1, 1,0,0,0,1, 0,0,0,0,0};
int N[25] = {1,1,1,1,1, 0,1,0,0,0, 0,0,1,0,0, 0,0,0,1,0, 1,1,1,1,1};
int E[25] = {1,1,1,1,1, 1,0,1,0,1, 1,0,1,0,1, 1,0,1,0,1, 0,0,0,0,0};
int A[25] = {0,1,1,1,1, 1,0,1,0,0, 1,0,1,0,0, 0,1,1,1,1, 0,0,0,0,0};
int R[25] = {1,1,1,1,1, 1,0,1,0,0, 1,0,1,1,0, 0,1,0,1,1, 0,0,0,0,0};
int S[25] = {0,1,0,0,1, 1,0,1,0,1, 1,0,1,0,1, 1,0,0,1,0, 0,0,0,0,0};
int P[25] = {1,1,1,1,1, 1,0,1,0,0, 1,0,1,0,0, 0,1,0,0,0, 0,0,0,0,0};
int C[25] = {0,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 1,0,0,0,1, 0,0,0,0,0};
int space[25] = {0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0};

int letter_time;
int write_time;

void setup() {
  //use pins 0-4 for output
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);

  letter_time = 2; //delay between letters (ms)
  write_time = 1; //how long a led is activated for (ms)
}

//Write an letter to the POV display
void write_letter(int * letter) {
  //write letter, column by column
  for(int i = 0; i < 5; i++) {
    for(int j = 0; j < 5; j++) {
      digitalWrite(j, letter[j+i*5]);
    }
    delay(write_time);
  }

  //write space after letter
  for(int i = 0; i < 5; i++) {
    digitalWrite(i, 0);
  }
  delay(letter_time);
}

void loop() {
  write_letter(B);
  write_letter(O);
  write_letter(V);
  write_letter(I);
  write_letter(N);
  write_letter(E);
  write_letter(space);
  write_letter(A);
  write_letter(E);
  write_letter(R);
  write_letter(O);
  write_letter(space);
}

rNOMADS 2.0.2 released

I uploaded the newest version of rNOMADS to CRAN yesterday. This one has a new plotting function for wind altitude azimuth, and magnitude (see below for plot and source code). I also added a function for reading GRIB inventories, fixed a few typos, and tweaked a few functions. In other news, the rNOMADS subversion repository has migrated to R-Forge: http://rnomads.r-forge.r-project.org/.

I’ve also set up an rNOMADS mailing list; subscribe here if you need to ask for help or want to hear about new releases.

Wind profiles at each infrasound station in the Transportable Array (currently on the US East Coast). Ground surface is at the center of the plot, the top of the stratosphere is at the outer radius. Wind magnitudes are denoted by color, and azimuth by position around the circle.

Wind profiles at each infrasound station in the Transportable Array (currently on the US East Coast). Ground surface is at the center of the plot, the top of the stratosphere is at the outer radius. Wind magnitudes are denoted by color, and azimuth by position around the circle.


library(rNOMADS)

download.file("http://www.unc.edu/~haksaeng/rNOMADS/myTA.RDATA",
destfile = "myTA.RDATA")
load("myTA.RDATA")
#Find the latest Global Forecast System model run
model.urls <- GetDODSDates("gfs_hd")
latest.model <- tail(model.urls$url, 1)
model.runs <- GetDODSModelRuns(latest.model)
latest.model.run <- tail(model.runs$model.run, 1)

#Get model nodes

lons <- seq(0, 359.5, by = 0.5)
lats <- seq(-90, 90, by = 0.5)
lon.ind <- which(lons &lt;= max(myTA$lon + 360) &amp; lons &gt;= min(myTA$lon + 360)) - 1
lat.ind <- which(lats &lt;= max(myTA$lat) &amp; lats &gt;= min(myTA$lat)) - 1
levels <- c(0, 46)
time <- c(0, 0)

#Get data
hgt.data <- DODSGrab(latest.model, latest.model.run, "hgtprs",
    time, c(min(lon.ind), max(lon.ind)), c(min(lat.ind), max(lat.ind)), levels)
ugrd.data <- DODSGrab(latest.model, latest.model.run, "ugrdprs",
    time, c(min(lon.ind), max(lon.ind)), c(min(lat.ind), max(lat.ind)), levels)
vgrd.data <- DODSGrab(latest.model, latest.model.run, "vgrdprs",
    time, c(min(lon.ind), max(lon.ind)), c(min(lat.ind), max(lat.ind)), levels)

#Reformat the data
hgt.grid <- ModelGrid(hgt.data, c(0.5, 0.5))
ugrd.grid <- ModelGrid(ugrd.data, c(0.5, 0.5))
vgrd.grid <- ModelGrid(vgrd.data, c(0.5, 0.5))

#Build profiles
zonal.wind <- c()
meridional.wind <- c()
height >- c()
for(k in 1:length(myTA$lon)) {
     hgt.profile <- BuildProfile(hgt.grid,
         myTA$lon[k], myTA$lat[k], spatial.average = TRUE)
    ugrd.profile <- BuildProfile(ugrd.grid,
        myTA$lon[k], myTA$lat[k], spatial.average = TRUE)
    vgrd.profile <- BuildProfile(vgrd.grid,
        myTA$lon[k], myTA$lat[k], spatial.average = TRUE)
    synth.hgt <- seq(min(hgt.profile),
        max(hgt.profile), length.out = 1000)
    ugrd.spline <- splinefun(hgt.profile, ugrd.profile, method = "natural")
    vgrd.spline <- splinefun(hgt.profile, vgrd.profile, method = "natural")
    zonal.wind[[k]] <- ugrd.spline(synth.hgt)
    meridional.wind[[k]] <- vgrd.spline(synth.hgt)
    height[[k]] <- synth.hgt
     print(paste("Finished", k, "of", length(myTA$lon), "profiles!"))
}

#Plot them all
PlotWindProfile(zonal.wind, meridional.wind, height, lines = TRUE, points = FALSE,
    elev.circles = c(0, 25000, 50000), elev.labels = c(0, 25, 50), radial.lines = seq(45, 360, by = 45),
    colorbar = TRUE, invert = FALSE, point.cex = 2, pch = 19, lty = 1, lwd = 1,
    height.range = c(0, 50000), colorbar.label = "Wind Speed (m/s)", elev.labels.az = 15)

A flight aboard the NASA High Altitude Student Platform (HASP)

The NASA High Altitude Student Payload (HASP) project provides a spot on a high altitude balloon payload for undergraduate and graduate students. When I heard about this last year, I gathered a team together, we applied and were accepted into the program. Our project: launch infrasound microphones into the stratosphere. Infrasound (sound at frequencies below audio range) is usually measured at the Earth’s surface, but we know it propagates hundreds of kilometers upward into the atmosphere. Our goal is to measure these sound waves as they cross the stratosphere.

The HASP project was definitely a commitment. As the team leader, I was required to write a monthly status report letting the HASP project leaders know what I was up to. I had to build my payload box under strict power draw, weight, and size limits. I also had to learn electronics from the ground up. Thankfully, another member of our team had lots of experience in electronics, so it wasn’t so bad.

Our Omnirecs DataCube logger installed in the payload box.

Our Omnirecs DataCube logger installed in the payload box.

A few weeks ago, I traveled to Palestine, Texas to bring my payload to the Columbia Scientific Balloon Facility (CSBF). There, our payload was subjected to extreme temperatures (ranging from -50 to 50 Celsius) and pressures (sea level to stratospheric).  We passed the test, recording the 8 Hz signal from the vacuum pump clearly even when the pressure was around 5% of sea level.  This was an important milestone: not only did it clear us for flight, but it also showed that our differential pressure microphones (constructed by Dr. Jeff Johnson at Boise State University) would operate in a near vacuum, something they were not designed to do.

Our payload about to face the thermal/vacuum test.

Our payload (the white box with the UNC logo) about to face the thermal/vacuum test.

From Texas, I traveled to New Mexico to launch our payload into the stratosphere.  I ended up staying in my home town, about 2.5 hours from the launch site at (a CSBF facility in Ft. Sumner, NM).  Needless to say, I spent a lot of time on the road!  I had to drive there and back three times: once to put my microphones on the flight ladder (see below), another time to make sure everything worked during the “hang test” (a dry run for launch), and finally for the big day itself – the flight.

CSBF staff mounting the infrasound microphones on the flight ladder.

CSBF staff mounting the infrasound microphones on the flight ladder.

Like any balloon flight, this one depended on the weather.  This time, the news was not good.  One group was ahead of HASP, and they had dibs on each launch window.  They tried twice, and were not able to fly both times.  I had to fly home on Saturday, so I showed CSBF and HASP personnel how to set up my payload, and I resigned myself to not seeing the balloon fly.

But as luck would have it, the previous team decided to wait, and a launch window opened Saturday morning, the day I was scheduled to fly home.  Since the flight was early in the morning and my plane ticket was for early afternoon, I decided I was going to go see the launch.  I drove out, arrived in Ft. Sumner at about 10 PM, slept in the back of the car for a few hours, then got up at 2:45 AM Saturday morning to start getting ready.

It's about 3:45 AM, and CSBF staff secure the microphones after I powered them up about 15 minutes before.  The wheel on "Big Bill" (the launch vehicle), is taller than I am.

It’s about 3:45 AM, and CSBF staff secure the microphones after I power them up. The wheel on “Big Bill” (the payload vehicle), is taller than I am.

The launch was touch and go the whole time – we had to wait for the winds to all blow in the same direction for the first 1000 ft in order to start the inflation process.  As luck would have it, they did straighten out, and the call was given to roll out and start inflating the balloon.

The balloon reaches full inflation about a half hour after sunrise.

The balloon reaches full inflation about a half hour after sunrise.

The launch was spectacular.  The balloon was released and drifted into the air.  Big Bill started driving in the direction that the balloon was going, and just when it was overhead, the payload was released.  The entire structure (800 ft high!) was now in free flight.  It seemed to climb slowly, but that was an illusion – when the balloon was 12,000 ft above the ground it still seemed close enough to touch.

The radio crackles and says "it's your balloon."  With that, the 3 million cubic ft envelope is released and begins climbing into the air.

The radio crackles and says “it’s your balloon.” With that, the 3 million cubic ft envelope is released and begins climbing into the air.

Here we go!

Here we go!

 

The balloon flew for about 8 hours, and was terminated over northeast Arizona.  Once the recovery team picks it up and ships us our data logger, we can find out what we heard up there.

A big thanks to the Louisiana State team for running HASP, and all the great people at CSBF who made it all happen!

The Search for our Missing Balloon: Closing in on the Landing Zone

In late May, Jared Sabater of Soleil Multimedia and I launched a high altitude balloon from south Chapel Hill, North Carolina. The balloon was carrying three cameras to capture spectacular high resolution video images of North Carolina from 20 miles in the air. We also expect to see the black sky of near space and a slightly curved horizon. However, the satellite tracker package fell off immediately after launch, and the balloon — cameras in tow — disappeared into the sky.

We set off to the expected landing zone — Harnett County, North Carolina. Needless to say, the search made looking for a needle in a haystack sound easy, and we returned home with nothing. I posted a desperate plea on my blog (read it here) and crossed my fingers, hoping someone would find the payload and get in contact with me. A reporter from the Daily Record in Dunn, North Carolina, came across my blog post and wrote a story about it.  A day later, I got a call from a woman living in Coats, North Carolina.

“I saw your balloon,” she said.

I didn’t believe her at first.  The balloon was supposed to pop in the stratosphere, not come down intact.  She said she saw it at sunset, a full twelve hours after launch — I figured it should have been way out in the Atlantic by then.  But as she described what she saw, I realized that there were only two possibilities:
1.  She saw our balloon.

2.  Someone else launched a high altitude balloon that just happened to come down in Coats.

The probability of #2 is vanishingly small — smaller even than #1, so I was forced to conclude that, in fact, our balloon was somewhere by Coats.  Jared, my friend Xiao, and I drive out to Coats to talk with the witness.  She was wonderfully nice and amazingly observant.  We stood by the window from which she saw the balloon, and she started telling me what she saw.  First of all, her pastor had also seen it crossing a road to the north, so we had a bearing.  Second, she said it had something sticking off to the side.  Third, it had remained in the same place for about an hour.

I didn’t know what to make of the second and third statements.  Balloons don’t fly with things sticking sideways, that makes them unbalanced and they tend to rotate so that whatever it is sticks straight down.  Also, even when the wind is imperceptible at the Earth’s surface, it’s strong enough above the surface to move the balloon out of her viewing area in less than an hour.

That’s when I realized what she’d seen:  Our balloon had already landed by the time she glanced out her window!!

It was simple in retrospect.  By the time the witness looked out her window and saw the balloon, the payload was on the ground or snagged in a tree.  The balloon envelope was still buoyant, hanging above the landing site like a giant flag — pulled slightly sideways and hooked over by the wind (hence the thing sticking out) and more or less stationary (why she was able to see it for an hour in the same place).

Here’s what we know right now:

The balloon envelope is cream to reddish colored and probably up to 20′ across.  It’s likely shredded into bits due to UV light and wind action.  The payload is 40′ from the envelope in a red lunch box with cameras attached.  The envelope is likely draped over a tree, and the payload is probably in the upper branches of another tree.

The area visible to the witness starts at 35.389756 N latitude, 78.638441 W longitude, and extends due east.  We can define sight boundaries by using buildings and trees that obscure her view to the east and the west (see figures below).

We searched several fields in the sight line and didn’t find anything, so the payload’s likely in forested areas.

Another witness saw the balloon crossing Red Hill Church Road at very low elevation, heading towards the first witness’ house, so that road defines a hard eastern boundary to the search area.

The first witness says the balloon was west of Black River – this provides another search area boundary, but with less confidence.

Elevation profiles (see below) and distances to the Black River make it even more unlikely that the witness could have seen the balloon on the ground if it were in the Black River valley.

Here are a series of maps of the search area:

view_areaThe red triangle defines the region visible from the first witness’ house, with the east boundary defined from the road which the balloon crossed per the second witness’ description.  Green polygons enclosed regions we were able to search on our first trip out to Coats.  The pale blue arc is 1 mile from the first witness’ house.  It’s hard to imagine being able to make out as much detail as she reported if the balloon is beyond this circle.

Elevation profiles along the north and south sight lines are available here:

north

south

These profiles suggest that the Black River area would be difficult to see from the first witness’ location.

landing_zone

The four yellow polygons show the most likely landing zones based on the analyses described above.  Areas A and B are by far the most probable locations for the payload, based on the witness’ statements and distance from where she saw it.  Any search should carefully investigate these regions.  Area C is also possible, but rather less probable because it is significantly below (and thus probably invisible from) the witness’ house.  Also, it’s quite far away, which would make the details she described hard to see.  Finally Area D cannot be eliminated, but it is the least likely due to elevation and distance.

Our next step is to go out and search.  If you’d like to join, or if you have some other information for us, let us know!

Extracting North American Mesoscale (NAM) Model Data with rNOMADS using DODS

The (relatively) recent upgrade of rNOMADS to version 2.0.0 allows cross platform support for downloading atmospheric and oceanic operational model data into R via the GrADS-DODS system.  However, users have been experiencing difficulty using DODS to get North American Mesoscale (NAM) real time data.  The script below shows an example of getting the NAM 12z model for North America using DODS.  Below the script and the figure, I’ll talk about why getting NAM data via DODS is different than, say, GFS.

#Get data for NAM subsets using DODS
library(rNOMADS)
library(rgl)
#Individual NAM models are listed separately in GRIB but all together in DODS.
#So we get the dates of the NAM runs first, then we have to sort out which "type" of NAM we want.

all.nam.dates <- GetDODSDates("nam", request.sleep = 1)

#Here, we pull the model runs.  This is when we can get individual models out.
#Get the most recent one:
all.nam.mr <- GetDODSModelRuns(tail(all.nam.dates$url, 1))

#For example, let's get North America "nam_na" model, 12z model run.
namna.model.runs <- all.nam.mr$model.run[grepl("nam_na_12z", all.nam.mr$model.run)]

#We can get info on what that model contains by inspecting the output from this:
info <- GetDODSModelRunInfo(tail(all.nam.dates$url, 1), tail(namna.model.runs, 1))

#Get data on surface solar flux across North America
sf.data <- DODSGrab(tail(all.nam.dates$url, 1), tail(namna.model.runs, 1), "csdsfsfc", 0, c(0,707), c(0, 286))

#Now we can plot it with rgl (do NOT use ModelGrid, it has the wrong projection)
#First, set the "missing data" values to -1 (or whatever)

#Credit to Dr. David Forrest for this display code:
valScrubbed<-as.numeric(sf.data$value)
valScrubbed[valScrubbed==9.999e+20]<- -1
with(sf.data,plot3d(lon,lat,valScrubbed,col=heat.colors(100)[cut(valScrubbed,breaks=100)],asp=c(1/cos(35*pi/180),1,.2)))

Solar radiation flux over North America from the NAM NA model.

Solar radiation flux over North America from the NAM NA model.


The reason we can’t just get a specific NAM subset model using (for example)

GetDODSDates("nam_na", request.sleep = 1)

is because the DODS server is set up differently than the GRIB server for NAM. It turns out that all the NAM models are stored together in one directory (in GRIB, each one has its own directory), and so we actually have to sort out individual models at the model run level, not the date level. So the way the above script deals with this is:
1. Figure out all dates when NAM was run.
2. Figure out the model runs for a specific date.
3. Pull out the specific NAM model subset you want (e.g. nam_na, nam_ak, whatever).
4. Get the model data.

If you have questions, don’t hesitate to contact me:

rNOMADS 2.0.1 released

The latest version of rNOMADS is now available on CRAN.  This update resolves several minor bugs and one major one involving multiple variable/level selections when using the ModelGrid function.  I have also added support for two more models on NOMADS:  Climate Forecast System Flux Products and Climate Forecast System 3D Pressure Products.  This brings the total number of real time model products supported by rNOMADS to 57.  A quick test of the Climate Forecast System Flux Products produced the following image of surface albedo for 1200 GMT on July 4th, 2014:

Albedo of the Earth's surface at 1200 GMT, July 4th, 2014.

Albedo of the Earth’s surface at 1200 GMT, July 4th, 2014.

In other news, data curators at the University Corporation for Atmospheric Research (UCAR) are currently investigating rNOMADS as an open source software package to read their GRIB archive.  UCAR hosts a series of model archive data, some of which is freely available to the public.  Their specific interest is in using rNOMADS to read their archive of the FNL model.  They have gotten wgrib2 to work on MAC OS X, allowing rNOMADS GRIB functionality to be extended to Macintosh machines.

Downloading weather, sea ice, and wave model data with the rNOMADS package in R

The NOAA Operational Model Archive and Distribution System is a treasure trove of near real time and archived model outputs describing global and regional weather, sea ice, and wave data.  I developed the rNOMADS package about a year ago to make this available to R users. In this post, I’ll present some source code and a couple of figures showing a few of the useful things you can do with rNOMADS.

For detailed examples showing rNOMADS with GRIB file support (Linux only) see the vignette here, for a cross platform version of the same, see here.

1.  Getting wind speed at a specific point

library(rNOMADS)

#A location near my house
lat <- 35.828304
lon <- -79.107467

#Find the latest Global Forecast System model run
model.urls <- GetDODSDates("gfs_hd")
latest.model <- tail(model.urls$url, 1)
model.runs <- GetDODSModelRuns(latest.model)
latest.model.run <- tail(model.runs$model.run, 1)

#Get nearest model nodes
lons <- seq(0, 359.5, by = 0.5)
lats <- seq(-90, 90, by = 0.5)
lon.diff <- abs(lon + 360 - lons)
lat.diff <- abs(lat - lats)
model.lon.ind <- which(lon.diff == min(lon.diff)) - 1 #NOMADS indexes at 0
model.lat.ind <- which(lat.diff == min(lat.diff)) - 1 

#Subset model
time <- c(0,0) #Model status at initialization
lon.inds <- c(model.lon.ind - 2, model.lon.ind + 2)
lat.inds <- c(model.lat.ind - 2, model.lat.ind + 2)
variable <- "ugrd10m" #First get east-west wind

u.grd.data <- DODSGrab(latest.model, latest.model.run, variable,
   time, lon.inds, lat.inds)

variable <- "vgrd10m" #Then get north-south wind

v.grd.data <- DODSGrab(latest.model, latest.model.run, variable,
   time, lon.inds, lat.inds)

#Reformat the data
u.grd.grid <- ModelGrid(u.grd.data, c(0.5, 0.5))
v.grd.grid <- ModelGrid(v.grd.data, c(0.5, 0.5))

#Interpolate it to the point of interest
u.point <- BuildProfile(u.grd.grid, lon, lat, TRUE)
v.point <- BuildProfile(v.grd.grid, lon, lat, TRUE)

#Present results!
print(paste("The East-West winds at Briar Chapel are going", sprintf("%.2f", u.point),
    "meters per second, and the north-south winds are going", sprintf("%.2f", v.point),
    "meters per second."))

#How did I know all these strange parameter names?
info <- GetDODSModelRunInfo(latest.model, latest.model.run)
print(info)

2. Getting a temperature profile from 0 to 40 km above a specific point

library(rNOMADS)

#A location near my house
lat <- 35.828304
lon <- -79.107467

#Find the latest Global Forecast System model run
model.urls <- GetDODSDates("gfs_hd")
latest.model <- tail(model.urls$url, 1)
model.runs <- GetDODSModelRuns(latest.model)
latest.model.run <- tail(model.runs$model.run, 1)

#Get nearest model nodes
lons <- seq(0, 359.5, by = 0.5)
lats <- seq(-90, 90, by = 0.5)
lon.diff <- abs(lon + 360 - lons)
lat.diff <- abs(lat - lats)
model.lon.ind <- which(lon.diff == min(lon.diff)) - 1 #NOMADS indexes at 0
model.lat.ind <- which(lat.diff == min(lat.diff)) - 1 

#Subset model
time <- c(0,0) #Model status at initialization
lon.inds <- c(model.lon.ind - 2, model.lon.ind + 2)
lat.inds <- c(model.lat.ind - 2, model.lat.ind + 2)
levels <- c(0, 46) #All pressure levels
variable <- "tmpprs" #First get temperature

tmpprs.data <- DODSGrab(latest.model, latest.model.run, variable,
   time, lon.inds, lat.inds, levels)

variable <- "hgtprs" #Now get elevation of each pressure level
hgtprs.data <- DODSGrab(latest.model, latest.model.run, variable,
   time, lon.inds, lat.inds, levels)

#Reformat the data
tmpprs.grid <- ModelGrid(tmpprs.data, c(0.5, 0.5))
hgtprs.grid <- ModelGrid(hgtprs.data, c(0.5, 0.5))

#Interpolate it to the point of interest
tmpprs.point <- BuildProfile(tmpprs.grid, lon, lat, TRUE)
hgtprs.point <- BuildProfile(hgtprs.grid, lon, lat, TRUE)

#Plot it!
plot(tmpprs.point - 272.15, hgtprs.point, xlab = "Temperature (C)",
   ylab = "Elevation (m)", main = paste("Temperature above Chapel Hill, NC, at",
   tmpprs.grid$model.run.date))
Temperature profile of troposphere and stratosphere above Chapel Hill, NC, on May 28.

Temperature profile of troposphere and stratosphere above Chapel Hill, NC, on May 28.

2. A world map of surface temperature

library(GEOmap)
library(rNOMADS)

model.urls <- GetDODSDates("gfs_hd")
latest.model <- tail(model.urls$url, 1)
model.runs <- GetDODSModelRuns(latest.model)
latest.model.run <- tail(model.runs$model.run, 1)

time <- c(0,0) #Analysis model
lon <- c(0, 719) #All 720 longitude points
lat <- c(0, 360) #All 361 latitude points

tmp2m.data <- DODSGrab(latest.model, latest.model.run,
    "tmp2m", time, lon, lat)
atmos <- ModelGrid(tmp2m.data, c(0.5, 0.5), "latlon")
colormap <- rev(rainbow(500, start = 0 , end = 5/6))
image(atmos$x, sort(atmos$y), atmos$z[1,1,,], col = colormap,
    xlab = "Longitude", ylab = "Latitude",
    main = paste("World Temperature at Ground Level:", atmos$model.run.date))
plotGEOmap(coastmap, border = "black", add = TRUE,
    MAPcol = NA)
World temperature on May 28.

World temperature on May 28.

2. Wave heights in the northwest Atlantic ocean


library(rNOMADS)
library(GEOmap)
library(RCurl)

model.urls <- GetDODSDates("wave")
latest.model <- tail(model.urls$url, 1)
model.runs <- GetDODSModelRuns(latest.model) #West Atlantic waves
latest.model.run <- tail(model.runs$model.run, 1)
time <- c(0,0)
lon <- c(0, 274)
lat <- c(0, 202)

wave.data <- DODSGrab(latest.model, latest.model.run,
    "htsgwsfc", time, lon, lat)
wave.grid <- ModelGrid(wave.data, c(0.25, 0.25))
#Remove "no data" values
wave.grid$z[which(wave.grid$z>1e10, arr.ind=TRUE)] <- NA
colormap <- rainbow(500, start=0, end=5/6)
image(wave.grid$x, sort(wave.grid$y), wave.grid$z[1,1,,], col = colormap,
xlab = "Longitude", ylab = "Latitude",
main = paste("Wave Height:", wave.grid$model.run.date))
plotGEOmap(coastmap, border = "black", add = TRUE,
MAPcol = "black")

Wave heights in the north Atlantic on July 4th, 2014. High seas from Hurricane Arthur are visible just off the US East Coast.