Flight Summary

Flight Statistics

Data normalized across several flight logs, spanning from NS45 to NS80, excepting NS54, NS55, and NS68 (NS54 is reserved for a flight still in development, and both NS55 and NS68 failed to record location data).

Due to a settings issue, before NS59, the cell tracker hardware stopped recording above 12.1 km (except for NS53). This limit is shown as a grey horizontal line in the figures depicting altitude.

require(ggplot2)
Loading required package: ggplot2
require(directlabels)
Loading required package: directlabels
require(scales)
Loading required package: scales
source("setup.R")
flight_times <- read_flight_times("flight_times.txt")
flight_data <- read_flight_data("flight_data/", calculate_difftime = TRUE)

1 Ascent Rate

1.1 Ascent Rate vs Time of Year

ggplot(flight_data,
       aes(
           x = as.Date(format(DateTime, format = "%m-%d"), format = "%m-%d"),
           y = Ascent_Rate_m_s,
           colour = Flight
       )) +
    geom_point() + geom_path() +
    geom_hline(yintercept = 0, alpha = 0.2) +
    scale_x_date(
        date_breaks = "1 month",
        labels = date_format("%b"),
        limits = as.Date(c("01-01", "12-31"), format = "%m-%d"),
        expand = c(0, 0)
    ) +
    geom_dl(aes(label = gsub("NS", "", Flight)),
            method = list(cex = 0.8, dl.trans(y = y + 0.2), "top.bumpup")) + guides(colour = FALSE) +
    labs(title = "Ascent Rate vs Time of Year", x = "Time of Year", y = "Ascent Rate (m/s)")

1.2 Ascent Rate After Burst

ggplot(flight_data,
       aes(x = burst_difftime / 60,
           y = Ascent_Rate_m_s,
           colour = Flight)) +
    geom_path() +
    xlim(-0.5, 3) +
    geom_dl(aes(label = gsub("NS", "", Flight)), method = list("smart.grid", "small.box.labels")) + guides(colour = FALSE) +
    labs(title = "Ascent Rate After Burst", x = "Time Since Burst (minutes)", y = "Ascent Rate (m/s)")

1.3 Ascent Rate vs Altitude

ggplot(flight_data,
       aes(x = Altitude_m / 1000,
           y = Ascent_Rate_m_s,
           colour = Flight)) +
    geom_path() +
    geom_dl(aes(label = gsub("NS", "", Flight)),
            method = list("far.from.others.borders", "small.box.labels")) + guides(colour = FALSE) +
    labs(title = "Ascent Rate vs Altitude", x = "Altitude (km)", y = "Ascent Rate (m/s)")

2 Ground Speed

2.1 Ground Speed vs Time of Year

ggplot(flight_data,
       aes(
           x = as.Date(format(DateTime, format = "%m-%d"), format = "%m-%d"),
           y = Ground_Speed_m_s,
           colour = Flight
       )) +
    geom_point() + geom_path() +
    scale_x_date(
        date_breaks = "1 month",
        labels = date_format("%b"),
        limits = as.Date(c("01-01", "12-31"), format = "%m-%d"),
        expand = c(0, 0)
    ) +
    geom_dl(aes(label = gsub("NS", "", Flight)),
            method = list(cex = 0.8, dl.trans(y = y + 0.2), "top.bumpup")) + guides(colour = FALSE) +
    labs(title = "Ground Speed vs Time of Year", x = "Time of Year", y = "Ground Speed (m/s)")

2.2 Ground Speed vs Altitude

ggplot(
    flight_data,
    aes(
        x = Altitude_m / 1000,
        y = Ground_Speed_m_s,
        colour = as.POSIXlt(DateTime)$yday
    )
) +
    geom_point() +
    scale_colour_gradient2(
        limits = c(0, 366),
        low = "green",
        mid = "red",
        midpoint = 366 / 2,
        high = "blue"
    ) +
    labs(
        title = "Ground Speed vs Altitude",
        x = "Altitude (km)",
        y = "Ground Speed (m/s)",
        colour = "Day of Year"
    ) +
    theme(
        legend.position = c(1, 1),
        legend.direction = "horizontal",
        legend.justification = "right"
    )

Green: late winter, Yellow: spring, Red: summer, Purple: autumn, Blue: early winter

3 Ascent and Descent Durations

For flights with definite burst and landing times, we can extract the times it took for the balloon to ascend and descend from max altitude.

flight_durations <- data.frame()
for (current_flight in flight_times$Flight)
{
    if (!is.na(flight_times$Launch_Time[flight_times$Flight == current_flight]) &&
        !is.na(flight_times$Burst_Time[flight_times$Flight == current_flight]) &&
        !is.na(flight_times$Landing_Time[flight_times$Flight == current_flight]))
    {
        current_ascent_duration <-
            as.numeric(with(
                flight_times,
                difftime(Launch_Time[Flight == current_flight], Burst_Time[Flight == current_flight], units = "mins")
            )) * -1
        current_descent_duration <-
            as.numeric(with(
                flight_times,
                difftime(Landing_Time[Flight == current_flight], Burst_Time[Flight == current_flight], units = "mins")
            ))
        
        flight_durations <- rbind(
            flight_durations,
            data.frame(
                Flight = current_flight,
                Date = flight_times$Date[flight_times$Flight == current_flight],
                Ascent_Duration_min = current_ascent_duration,
                Descent_Duration_min = current_descent_duration,
                Ratio = current_ascent_duration / current_descent_duration
            )
        )
    }
}
rm(current_flight,
   current_ascent_duration,
   current_descent_duration)
rmarkdown::paged_table(flight_durations)

On average, ascent takes 69 minutes, while descent takes 36 minutes, with a ratio of 2.

3.1 Ascent Duration vs Descent Duration

ggplot(
    flight_durations,
    aes(
        x = Descent_Duration_min,
        y = Ascent_Duration_min,
        colour = as.POSIXlt(Date)$yday
    )
) +
    geom_point(size = 0.1) + geom_abline(slope = 1:3) +
    scale_colour_gradient2(
        limits = c(0, 366),
        low = "green",
        mid = "red",
        midpoint = 366 / 2,
        high = "blue"
    ) +
    geom_dl(aes(label = gsub("NS", "", Flight)), method = list(cex = 0.8)) +
    labs(
        title = "Ascent Duration vs Descent Duration",
        x = "Descent Duration (minutes)",
        y = "Ascent Duration (minutes)",
        colour = "Day of Year"
    ) +
    coord_fixed(ratio = 1) +
    scale_x_continuous(breaks = seq(
        from = 0,
        to = max(flight_data$Downrange_Distance_m),
        by = 10
    )) +
    scale_y_continuous(breaks = seq(
        from = 0,
        to = max(flight_data$Altitude_m),
        by = 10
    )) +
    theme(
        legend.position = c(1, 0.96),
        legend.direction = "horizontal",
        legend.justification = "right"
    )

Green: late winter, Yellow: spring, Red: summer, Purple: autumn, Blue: early winter

LS0tCnRpdGxlOiAiRmxpZ2h0IEFuYWx5c2lzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jX2Zsb2F0OiAKICAgICAgICBjb2xsYXBzZWQ6IGZhbHNlCi0tLQoKW0ZsaWdodCBTdW1tYXJ5XShmbGlnaHRfc3VtbWFyeS5uYi5odG1sKQoKW0ZsaWdodCBTdGF0aXN0aWNzXShmbGlnaHRfc3RhdGlzdGljcy5uYi5odG1sKQoKRGF0YSBub3JtYWxpemVkIGFjcm9zcyBzZXZlcmFsIGZsaWdodCBsb2dzLCBzcGFubmluZyBmcm9tIGByIG1pbihsZXZlbHMoZmxpZ2h0X2RhdGEkRmxpZ2h0KSlgIHRvIGByIG1heChsZXZlbHMoZmxpZ2h0X2RhdGEkRmxpZ2h0KSlgLCBleGNlcHRpbmcgTlM1NCwgTlM1NSwgYW5kIE5TNjggKE5TNTQgaXMgcmVzZXJ2ZWQgZm9yIGEgZmxpZ2h0IHN0aWxsIGluIGRldmVsb3BtZW50LCBhbmQgYm90aCBOUzU1IGFuZCBOUzY4IGZhaWxlZCB0byByZWNvcmQgbG9jYXRpb24gZGF0YSkuCgpEdWUgdG8gYSBzZXR0aW5ncyBpc3N1ZSwgYmVmb3JlIE5TNTksIHRoZSBjZWxsIHRyYWNrZXIgaGFyZHdhcmUgc3RvcHBlZCByZWNvcmRpbmcgYWJvdmUgMTIuMSBrbSAoZXhjZXB0IGZvciBOUzUzKS4gVGhpcyBsaW1pdCBpcyBzaG93biBhcyBhIGdyZXkgaG9yaXpvbnRhbCBsaW5lIGluIHRoZSBmaWd1cmVzIGRlcGljdGluZyBhbHRpdHVkZS4KCmBgYHtyIHNldHVwfQpyZXF1aXJlKGdncGxvdDIpCnJlcXVpcmUoZGlyZWN0bGFiZWxzKQpyZXF1aXJlKHNjYWxlcykKCnNvdXJjZSgic2V0dXAuUiIpCgpmbGlnaHRfdGltZXMgPC0gcmVhZF9mbGlnaHRfdGltZXMoImZsaWdodF90aW1lcy50eHQiKQpmbGlnaHRfZGF0YSA8LSByZWFkX2ZsaWdodF9kYXRhKCJmbGlnaHRfZGF0YS8iLCBjYWxjdWxhdGVfZGlmZnRpbWUgPSBUUlVFKQpgYGAKCiMgQXNjZW50IFJhdGUKCiMjIEFzY2VudCBSYXRlIHZzIFRpbWUgb2YgWWVhcgoKYGBge3IgYXNjZW50X3JhdGVfdnNfdGltZV9vZl95ZWFyLCBmaWcuaGVpZ2h0PTcuNDE2NDA3OSwgZmlnLndpZHRoPTEyfQpnZ3Bsb3QoZmxpZ2h0X2RhdGEsCiAgICAgICBhZXMoCiAgICAgICAgICAgeCA9IGFzLkRhdGUoZm9ybWF0KERhdGVUaW1lLCBmb3JtYXQgPSAiJW0tJWQiKSwgZm9ybWF0ID0gIiVtLSVkIiksCiAgICAgICAgICAgeSA9IEFzY2VudF9SYXRlX21fcywKICAgICAgICAgICBjb2xvdXIgPSBGbGlnaHQKICAgICAgICkpICsKICAgIGdlb21fcG9pbnQoKSArIGdlb21fcGF0aCgpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGFscGhhID0gMC4yKSArCiAgICBzY2FsZV94X2RhdGUoCiAgICAgICAgZGF0ZV9icmVha3MgPSAiMSBtb250aCIsCiAgICAgICAgbGFiZWxzID0gZGF0ZV9mb3JtYXQoIiViIiksCiAgICAgICAgbGltaXRzID0gYXMuRGF0ZShjKCIwMS0wMSIsICIxMi0zMSIpLCBmb3JtYXQgPSAiJW0tJWQiKSwKICAgICAgICBleHBhbmQgPSBjKDAsIDApCiAgICApICsKICAgIGdlb21fZGwoYWVzKGxhYmVsID0gZ3N1YigiTlMiLCAiIiwgRmxpZ2h0KSksCiAgICAgICAgICAgIG1ldGhvZCA9IGxpc3QoY2V4ID0gMC44LCBkbC50cmFucyh5ID0geSArIDAuMiksICJ0b3AuYnVtcHVwIikpICsgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArCiAgICBsYWJzKHRpdGxlID0gIkFzY2VudCBSYXRlIHZzIFRpbWUgb2YgWWVhciIsIHggPSAiVGltZSBvZiBZZWFyIiwgeSA9ICJBc2NlbnQgUmF0ZSAobS9zKSIpCmBgYAoKIyMgQXNjZW50IFJhdGUgQWZ0ZXIgQnVyc3QKCmBgYHtyIGFzY2VudF9yYXRlX2FmdGVyX2J1cnN0LCBmaWcuaGVpZ2h0PTcuNDE2NDA3OSwgZmlnLndpZHRoPTEyfQpnZ3Bsb3QoZmxpZ2h0X2RhdGEsCiAgICAgICBhZXMoeCA9IGJ1cnN0X2RpZmZ0aW1lIC8gNjAsCiAgICAgICAgICAgeSA9IEFzY2VudF9SYXRlX21fcywKICAgICAgICAgICBjb2xvdXIgPSBGbGlnaHQpKSArCiAgICBnZW9tX3BhdGgoKSArCiAgICB4bGltKC0wLjUsIDMpICsKICAgIGdlb21fZGwoYWVzKGxhYmVsID0gZ3N1YigiTlMiLCAiIiwgRmxpZ2h0KSksIG1ldGhvZCA9IGxpc3QoInNtYXJ0LmdyaWQiLCAic21hbGwuYm94LmxhYmVscyIpKSArIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkgKwogICAgbGFicyh0aXRsZSA9ICJBc2NlbnQgUmF0ZSBBZnRlciBCdXJzdCIsIHggPSAiVGltZSBTaW5jZSBCdXJzdCAobWludXRlcykiLCB5ID0gIkFzY2VudCBSYXRlIChtL3MpIikKYGBgCgojIyBBc2NlbnQgUmF0ZSB2cyBBbHRpdHVkZQoKYGBge3IgYXNjZW50X3JhdGVfdnNfYWx0aXR1ZGUsIGZpZy5oZWlnaHQ9Ny40MTY0MDc5LCBmaWcud2lkdGg9MTJ9CmdncGxvdChmbGlnaHRfZGF0YSwKICAgICAgIGFlcyh4ID0gQWx0aXR1ZGVfbSAvIDEwMDAsCiAgICAgICAgICAgeSA9IEFzY2VudF9SYXRlX21fcywKICAgICAgICAgICBjb2xvdXIgPSBGbGlnaHQpKSArCiAgICBnZW9tX3BhdGgoKSArCiAgICBnZW9tX2RsKGFlcyhsYWJlbCA9IGdzdWIoIk5TIiwgIiIsIEZsaWdodCkpLAogICAgICAgICAgICBtZXRob2QgPSBsaXN0KCJmYXIuZnJvbS5vdGhlcnMuYm9yZGVycyIsICJzbWFsbC5ib3gubGFiZWxzIikpICsgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArCiAgICBsYWJzKHRpdGxlID0gIkFzY2VudCBSYXRlIHZzIEFsdGl0dWRlIiwgeCA9ICJBbHRpdHVkZSAoa20pIiwgeSA9ICJBc2NlbnQgUmF0ZSAobS9zKSIpCmBgYAoKIyBHcm91bmQgU3BlZWQKCiMjIEdyb3VuZCBTcGVlZCB2cyBUaW1lIG9mIFllYXIKCmBgYHtyIGdyb3VuZF9zcGVlZF92c190aW1lX29mX3llYXIsIGZpZy5oZWlnaHQ9Ny40MTY0MDc5LCBmaWcud2lkdGg9MTJ9CmdncGxvdChmbGlnaHRfZGF0YSwKICAgICAgIGFlcygKICAgICAgICAgICB4ID0gYXMuRGF0ZShmb3JtYXQoRGF0ZVRpbWUsIGZvcm1hdCA9ICIlbS0lZCIpLCBmb3JtYXQgPSAiJW0tJWQiKSwKICAgICAgICAgICB5ID0gR3JvdW5kX1NwZWVkX21fcywKICAgICAgICAgICBjb2xvdXIgPSBGbGlnaHQKICAgICAgICkpICsKICAgIGdlb21fcG9pbnQoKSArIGdlb21fcGF0aCgpICsKICAgIHNjYWxlX3hfZGF0ZSgKICAgICAgICBkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwKICAgICAgICBsYWJlbHMgPSBkYXRlX2Zvcm1hdCgiJWIiKSwKICAgICAgICBsaW1pdHMgPSBhcy5EYXRlKGMoIjAxLTAxIiwgIjEyLTMxIiksIGZvcm1hdCA9ICIlbS0lZCIpLAogICAgICAgIGV4cGFuZCA9IGMoMCwgMCkKICAgICkgKwogICAgZ2VvbV9kbChhZXMobGFiZWwgPSBnc3ViKCJOUyIsICIiLCBGbGlnaHQpKSwKICAgICAgICAgICAgbWV0aG9kID0gbGlzdChjZXggPSAwLjgsIGRsLnRyYW5zKHkgPSB5ICsgMC4yKSwgInRvcC5idW1wdXAiKSkgKyBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICAgIGxhYnModGl0bGUgPSAiR3JvdW5kIFNwZWVkIHZzIFRpbWUgb2YgWWVhciIsIHggPSAiVGltZSBvZiBZZWFyIiwgeSA9ICJHcm91bmQgU3BlZWQgKG0vcykiKQpgYGAKCiMjIEdyb3VuZCBTcGVlZCB2cyBBbHRpdHVkZQoKYGBge3IgZ3JvdW5kX3NwZWVkX3ZzX2FsdGl0dWRlLCBmaWcuaGVpZ2h0PTcuNDE2NDA3OSwgZmlnLndpZHRoPTEyfQpnZ3Bsb3QoCiAgICBmbGlnaHRfZGF0YSwKICAgIGFlcygKICAgICAgICB4ID0gQWx0aXR1ZGVfbSAvIDEwMDAsCiAgICAgICAgeSA9IEdyb3VuZF9TcGVlZF9tX3MsCiAgICAgICAgY29sb3VyID0gYXMuUE9TSVhsdChEYXRlVGltZSkkeWRheQogICAgKQopICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQyKAogICAgICAgIGxpbWl0cyA9IGMoMCwgMzY2KSwKICAgICAgICBsb3cgPSAiZ3JlZW4iLAogICAgICAgIG1pZCA9ICJyZWQiLAogICAgICAgIG1pZHBvaW50ID0gMzY2IC8gMiwKICAgICAgICBoaWdoID0gImJsdWUiCiAgICApICsKICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiR3JvdW5kIFNwZWVkIHZzIEFsdGl0dWRlIiwKICAgICAgICB4ID0gIkFsdGl0dWRlIChrbSkiLAogICAgICAgIHkgPSAiR3JvdW5kIFNwZWVkIChtL3MpIiwKICAgICAgICBjb2xvdXIgPSAiRGF5IG9mIFllYXIiCiAgICApICsKICAgIHRoZW1lKAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMSwgMSksCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJyaWdodCIKICAgICkKYGBgCgpHcmVlbjogbGF0ZSB3aW50ZXIsIFllbGxvdzogc3ByaW5nLCBSZWQ6IHN1bW1lciwgUHVycGxlOiBhdXR1bW4sIEJsdWU6IGVhcmx5IHdpbnRlcgoKIyBBc2NlbnQgYW5kIERlc2NlbnQgRHVyYXRpb25zCgpGb3IgZmxpZ2h0cyB3aXRoIGRlZmluaXRlIGJ1cnN0IGFuZCBsYW5kaW5nIHRpbWVzLCB3ZSBjYW4gZXh0cmFjdCB0aGUgdGltZXMgaXQgdG9vayBmb3IgdGhlIGJhbGxvb24gdG8gYXNjZW5kIGFuZCBkZXNjZW5kIGZyb20gbWF4IGFsdGl0dWRlLiAKCmBgYHtyIGR1cmF0aW9uX3JhdGlvc30KZmxpZ2h0X2R1cmF0aW9ucyA8LSBkYXRhLmZyYW1lKCkKCmZvciAoY3VycmVudF9mbGlnaHQgaW4gZmxpZ2h0X3RpbWVzJEZsaWdodCkKewogICAgaWYgKCFpcy5uYShmbGlnaHRfdGltZXMkTGF1bmNoX1RpbWVbZmxpZ2h0X3RpbWVzJEZsaWdodCA9PSBjdXJyZW50X2ZsaWdodF0pICYmCiAgICAgICAgIWlzLm5hKGZsaWdodF90aW1lcyRCdXJzdF9UaW1lW2ZsaWdodF90aW1lcyRGbGlnaHQgPT0gY3VycmVudF9mbGlnaHRdKSAmJgogICAgICAgICFpcy5uYShmbGlnaHRfdGltZXMkTGFuZGluZ19UaW1lW2ZsaWdodF90aW1lcyRGbGlnaHQgPT0gY3VycmVudF9mbGlnaHRdKSkKICAgIHsKICAgICAgICBjdXJyZW50X2FzY2VudF9kdXJhdGlvbiA8LQogICAgICAgICAgICBhcy5udW1lcmljKHdpdGgoCiAgICAgICAgICAgICAgICBmbGlnaHRfdGltZXMsCiAgICAgICAgICAgICAgICBkaWZmdGltZShMYXVuY2hfVGltZVtGbGlnaHQgPT0gY3VycmVudF9mbGlnaHRdLCBCdXJzdF9UaW1lW0ZsaWdodCA9PSBjdXJyZW50X2ZsaWdodF0sIHVuaXRzID0gIm1pbnMiKQogICAgICAgICAgICApKSAqIC0xCiAgICAgICAgY3VycmVudF9kZXNjZW50X2R1cmF0aW9uIDwtCiAgICAgICAgICAgIGFzLm51bWVyaWMod2l0aCgKICAgICAgICAgICAgICAgIGZsaWdodF90aW1lcywKICAgICAgICAgICAgICAgIGRpZmZ0aW1lKExhbmRpbmdfVGltZVtGbGlnaHQgPT0gY3VycmVudF9mbGlnaHRdLCBCdXJzdF9UaW1lW0ZsaWdodCA9PSBjdXJyZW50X2ZsaWdodF0sIHVuaXRzID0gIm1pbnMiKQogICAgICAgICAgICApKQogICAgICAgIAogICAgICAgIGZsaWdodF9kdXJhdGlvbnMgPC0gcmJpbmQoCiAgICAgICAgICAgIGZsaWdodF9kdXJhdGlvbnMsCiAgICAgICAgICAgIGRhdGEuZnJhbWUoCiAgICAgICAgICAgICAgICBGbGlnaHQgPSBjdXJyZW50X2ZsaWdodCwKICAgICAgICAgICAgICAgIERhdGUgPSBmbGlnaHRfdGltZXMkRGF0ZVtmbGlnaHRfdGltZXMkRmxpZ2h0ID09IGN1cnJlbnRfZmxpZ2h0XSwKICAgICAgICAgICAgICAgIEFzY2VudF9EdXJhdGlvbl9taW4gPSBjdXJyZW50X2FzY2VudF9kdXJhdGlvbiwKICAgICAgICAgICAgICAgIERlc2NlbnRfRHVyYXRpb25fbWluID0gY3VycmVudF9kZXNjZW50X2R1cmF0aW9uLAogICAgICAgICAgICAgICAgUmF0aW8gPSBjdXJyZW50X2FzY2VudF9kdXJhdGlvbiAvIGN1cnJlbnRfZGVzY2VudF9kdXJhdGlvbgogICAgICAgICAgICApCiAgICAgICAgKQogICAgfQp9CgpybShjdXJyZW50X2ZsaWdodCwKICAgY3VycmVudF9hc2NlbnRfZHVyYXRpb24sCiAgIGN1cnJlbnRfZGVzY2VudF9kdXJhdGlvbikKCnJtYXJrZG93bjo6cGFnZWRfdGFibGUoZmxpZ2h0X2R1cmF0aW9ucykKYGBgCgpPbiBhdmVyYWdlLCBhc2NlbnQgdGFrZXMgYHIgc2lnbmlmKG1lYW4oZmxpZ2h0X2R1cmF0aW9ucyRBc2NlbnRfRHVyYXRpb25fbWluKSwgZGlnaXRzID0gMilgIG1pbnV0ZXMsIHdoaWxlIGRlc2NlbnQgdGFrZXMgYHIgc2lnbmlmKG1lYW4oZmxpZ2h0X2R1cmF0aW9ucyREZXNjZW50X0R1cmF0aW9uX21pbiksIGRpZ2l0cyA9IDIpYCBtaW51dGVzLCB3aXRoIGEgcmF0aW8gb2YgYHIgc2lnbmlmKG1lYW4oZmxpZ2h0X2R1cmF0aW9ucyRSYXRpbyksIGRpZ2l0cyA9IDIpYC4KCiMjIEFzY2VudCBEdXJhdGlvbiB2cyBEZXNjZW50IER1cmF0aW9uCgpgYGB7ciBjdXJyZW50X2FzY2VudF9kdXJhdGlvbl92c19jdXJyZW50X2Rlc2NlbnRfZHVyYXRpb24sIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD03LjQxNjQwNzl9CmdncGxvdCgKICAgIGZsaWdodF9kdXJhdGlvbnMsCiAgICBhZXMoCiAgICAgICAgeCA9IERlc2NlbnRfRHVyYXRpb25fbWluLAogICAgICAgIHkgPSBBc2NlbnRfRHVyYXRpb25fbWluLAogICAgICAgIGNvbG91ciA9IGFzLlBPU0lYbHQoRGF0ZSkkeWRheQogICAgKQopICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDAuMSkgKyBnZW9tX2FibGluZShzbG9wZSA9IDE6MykgKwogICAgc2NhbGVfY29sb3VyX2dyYWRpZW50MigKICAgICAgICBsaW1pdHMgPSBjKDAsIDM2NiksCiAgICAgICAgbG93ID0gImdyZWVuIiwKICAgICAgICBtaWQgPSAicmVkIiwKICAgICAgICBtaWRwb2ludCA9IDM2NiAvIDIsCiAgICAgICAgaGlnaCA9ICJibHVlIgogICAgKSArCiAgICBnZW9tX2RsKGFlcyhsYWJlbCA9IGdzdWIoIk5TIiwgIiIsIEZsaWdodCkpLCBtZXRob2QgPSBsaXN0KGNleCA9IDAuOCkpICsKICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiQXNjZW50IER1cmF0aW9uIHZzIERlc2NlbnQgRHVyYXRpb24iLAogICAgICAgIHggPSAiRGVzY2VudCBEdXJhdGlvbiAobWludXRlcykiLAogICAgICAgIHkgPSAiQXNjZW50IER1cmF0aW9uIChtaW51dGVzKSIsCiAgICAgICAgY29sb3VyID0gIkRheSBvZiBZZWFyIgogICAgKSArCiAgICBjb29yZF9maXhlZChyYXRpbyA9IDEpICsKICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoCiAgICAgICAgZnJvbSA9IDAsCiAgICAgICAgdG8gPSBtYXgoZmxpZ2h0X2RhdGEkRG93bnJhbmdlX0Rpc3RhbmNlX20pLAogICAgICAgIGJ5ID0gMTAKICAgICkpICsKICAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoCiAgICAgICAgZnJvbSA9IDAsCiAgICAgICAgdG8gPSBtYXgoZmxpZ2h0X2RhdGEkQWx0aXR1ZGVfbSksCiAgICAgICAgYnkgPSAxMAogICAgKSkgKwogICAgdGhlbWUoCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygxLCAwLjk2KSwKICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLAogICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gInJpZ2h0IgogICAgKQpgYGAKR3JlZW46IGxhdGUgd2ludGVyLCBZZWxsb3c6IHNwcmluZywgUmVkOiBzdW1tZXIsIFB1cnBsZTogYXV0dW1uLCBCbHVlOiBlYXJseSB3aW50ZXIKCmBgYHtyIHRlYXJkb3duLCBpbmNsdWRlPUZBTFNFfQpybShyZWFkX2ZsaWdodF90aW1lcywgcmVhZF9mbGlnaHRfZGF0YSkKCnJtKHNtYWxsLmJveC5sYWJlbHMpCiNybShmbGlnaHRfdGltZXMpCiNybShmbGlnaHRfZGF0YSkKYGBg