Subscribe Bookmark
Craige_Hales

Staff

Joined:

Mar 21, 2013

Space Station Video

Early Memory

When I was almost three, my Dad, W4FNB, SK, let me hang out in the radio room while he was listening to the signal from Sputnik.  I remember him explaining how the doppler shift required him to continuously adjust the tuning on the Hammarlund receiver HQ-129X, and how it was important for the receiver to warm up for several hours to stabilize the radio's internal oscillators.  I think he tried to show me something in the sky too, but it would be another four years before the school nurse realized how badly I needed glasses.  Sounds from the First Satellites 

More Recently

The International Space Station has been in orbit since 1998, or 2000.  This post presented JSL to fetch the ISS current location and plot it on a map built from another NASA image.  I tweaked the JSL a bit and made a video showing a couple of days of 90 minute orbits in less than a minute.

SpaceStation - YouTube

iss.jpg

A few improvements but pretty similar.  I messed up the file names by not filling in the leading zeros that Blender expects; I used JSL to rename the files.

// trimmed down JSON parser (go get the real one, there is more support for tables)

/*

* JSL JSON Parser, Xan Gregg, 2014-12-29

* pattern matching speedup, Craige Hales, 2015-01-01

*/

New Namespace("JSON",{});

JSON:Char Escape = Function({c},

  Match(c,

  "\!"", "\!"",

  "b", "\!u0008",

  "f", "\!u000C",

  "n", "\!u000A",

  "r", "\!u000D",

  "t", "\!u0009",

  c

  )

);

JSON:Unicode Escape = Function( {u},

  u = "\!"\!\!u" || u || "\!"";

  Parse( u );

);

JSON:Pattern Name =

  "\!"" // leading quotation mark

  + pattest( value = ""; 1) // init string accumulator

  + patrepeat(

  (Pat break( "\!\\!"" ) >> temp + Pat Test( value ||= temp; 1; )) // run of characters that are not \ or "

  |

  ("\u"+patlen(4)>>temp+pattest(value ||= JSON:Unicode Escape(temp);1)) // \uXXXX

  |

  ("\"+patlen(1)>>temp+pattest(value ||= JSON:Char Escape(temp);1)) // \", for example...embedded quotation mark

  )

  + "\!""; // trailing quotation mark

JSON:Pattern Number = patspan("0123456789-+eE.") >> temp + pattest(value=num(temp);1);

JSON:PatternParse = patpos(0) + patrepeat(patpos(/* remember how far we parsed for error message */)>>failpos

// /* comment out this line for a little more speed */+pattest(if(tickseconds()-lasttime>1,lasttime=tickseconds();wait(0));1)

+patfence(/* fence off previously parsed text...ain't no going back */)

+( patregex("\s*") // ignore whitespace

| JSON:Pattern Name

| ( ":" + pattest(key=value;1) )

| JSON:Pattern Number

| ("true" + pattest(value = 1;1))

| ("false" + pattest(value = 0;1))

| ("null" + pattest(value = .;1))

| ("[" + pattest(Insert Into( stack, {{}}, 1 );value = Empty();1))

| ("{" + pattest(Insert Into( stack, Associative Array(), 1 );value = Empty();Insert Into( keys, key, 1 );1))

| ("]" + pattest(If( !Is Empty( value ),Insert Into( stack[1], value, 1 /*needs reverse later*/ ));value = stack[1];value=reverse(value);Remove From( stack, 1 );1))

| ("}" + pattest(If( !Is Empty( value ),stack[1][key] = value);value = stack[1];Remove From( stack, 1 );key = keys[1];Remove From( keys, 1 );1))

| ("," + pattest(If( Is List( stack[1] ),Insert Into( stack[1], value, 1 /*needs reverse later*/ ),stack[1][key] = value);value = Empty();1))

)) + patrpos(0);

JSON:jsonParse = Function( {json},

  {stack = {}, keys = {}, key = "", value = Empty(), temp=0,totalLen = length(json),failpos=0},

  if( !try(patmatch(json,JSON:PatternParse),0), Throw( "unrecognized JSON at " || Char( failpos ) ));

  value;

);

// end namespace

lat = J( 3600, 1, . ); // trail of previous points, you can make it longer

lon = J( N Rows( lat ), 1, . );

idx = 1;

cur = idx;

error = "";

time = "";

dir="D:\directory\ISS\m";

seq=0;

/* open-notify.org (Thanks!) returns JSON data like this:

{

  "iss_position": {

    "latitude": 24.94415308475734,

    "longitude": 116.70173230986174

  },

  "message": "success",

  "timestamp": 1462990726

}

*/

updateLatLon = Function( {},

  {where = Try( Open( "http://api.open-notify.org/iss-now.json?" || Char( Random Integer( 1, 1e6 ) ) ), "" ), // fetch a JSON string

  locationData = JSON:jsonParse( where ) // use XAN's parser

  },

  If( !Is Empty( locationData ) & locationData["message"] == "success",

  error = ""; // clear any previous message

  lat[idx] = locationData["iss_position"]["latitude"]; // get the two numbers

  lon[idx] = locationData["iss_position"]["longitude"]; // used in the script below to plot a point

  time = locationData["timestamp"]; // unix time, not JMP time

  time = Format( 1jan1970 + time, /*"ddMonyyyyh:m:s"*/ "yyyy-mm-ddThh:mm:ss" ) || " GMT";

  cur = idx;

  idx++;

  If( idx > N Rows( lat ),

  idx = 1

  ); 

  ,

  error = "no location data available"

  );

  Try(

  g << inval;

  wait(1);

  seq++;

  g[framebox(1)]<<savepicture(dir||char(seq)||".png","png");

  ); // if the map exists, update it.

  Schedule( 28, updateLatLon() ); // schedule this function to run again. it would be rude to ask for updates much faster than this.

);

updateLatLon(); // bootstrap call, then it calls itself every 30 seconds

earthLight = Open( "http://eoimages.gsfc.nasa.gov/images/imagerecords/55000/55167/earth_lights_lrg.jpg", "jpg" );

{width, height} = earthLight << size; // need the ratio for the frame size...

New Window( "NASA's Night Light Map",

  g = Graph Box( framesize( width / 1, height / 1 ), X Scale( -180, 180 ), Y Scale( -90, 90 ),

  Text Color( "white" ); Text( {0, -50}, error ); Text( {-150, -88}, time ); Text( {-179, 80}, "International Space Station" );

  Text( RightJustified, {160, -88}, "http://visibleearth.nasa.gov" ); // credits

  Text( {-50, -88}, "http://open-notify.org/Open-Notify-API/ISS-Location-Now/" ); // credits

  //Marker Size( 2 ); Marker( Combine States( Color State( "RED" ), Marker State( 12 ) ), lon, lat ); // space station trail

  //Marker Size( 4 ); Marker( Combine States( Color State( "RED" ), Marker State( 12 ) ), {lon[cur], lat[cur]} ); // head

  bright = nrows(lon);

  Marker Size( 4 );

  for(i=cur, i>=1, i--,

  Marker( Combine States( Color State( RGBCOLOR(bright/nrows(lon),0.1,0.1) ), Marker State( 12 ) ), {lon, lat} );

  bright--;

  Marker Size( 2 );

  );

  for(i=nrows(lon),i>cur,i--,

  Marker( Combine States( Color State( RGBCOLOR(bright/nrows(lon),0.1,0.1) ), Marker State( 12 ) ), {lon, lat} );

  bright--;

  );

  )

);

frameBox = g[framebox( 1 )]; // the framebox, in the graphbox, is just inside the axes

frameBox << Background Map( Boundaries( "World" ) ); // add boundaries first; they wind up on top of the next bitmap...

g[axisbox( 1 )] << Scale( "Linear" ) << remove axis label; // the geo coordinates were set when the map was added, but

g[axisbox( 2 )] << Scale( "Linear" ) << remove axis label; // we need linear coords to line up the map...

frameBox << AddImage( Image( earthLight ), bounds( Left( -180 ), Right( 180 ), top( 90 ), bottom( -90 ) ) ); // -180..180 seems correct

imgSeg = frameBox << FindSeg( PictSeg( 1 ) ); // the most recently added one is first

imgSeg << lock( 1 ); // and lock it so mouse won't move it by mistake

frameBox << xaxis( show major ticks( false ), show minor ticks( false ), show labels( false ) );

frameBox << yaxis( show major ticks( false ), show minor ticks( false ), show labels( false ) );

Article Tags