cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Uncharted

Choose Language Hide Translation Bar
Craige_Hales
Super User
Space Station Video

update 15feb2018: see this cookbook item for an easier way

update 10apr2018: see Bryan's script for a modern approach

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.

 

 

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.

// code repaired 15feb2018, requires JMP 11 or later

// 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
+ Pat Test(
    value = "";
    1;
) // init string accumulator
+ Pat Repeat(
    (Pat Break( "\!\\!"" ) >> temp + Pat Test(
        value ||= temp;
        1;
    )) // run of characters that are not \ or "
    | ("\u" + Pat Len( 4 ) >> temp + Pat Test(
        value ||= JSON:Unicode Escape( temp );
        1;
    )) // \uXXXX
    | ("\" + Pat Len( 1 ) >> temp + Pat Test(
        value ||= JSON:Char Escape( temp );
        1;
    )) // \", for example...embedded quotation mark
) + "\!""; // trailing quotation mark
JSON:Pattern Number = Pat Span( "0123456789-+eE." ) >> temp + Pat Test(
    value = Num( temp );
    1;
);
JSON:PatternParse = Pat Pos( 0 ) + Pat Repeat(
    Pat Pos( /* 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)
    + Pat Fence( /* fence off previously parsed text...ain't no going back */ ) + (Pat Regex( "\s*" ) // ignore whitespace
    | JSON:Pattern Name | (":" + Pat Test(
        key = value;
        1;
    )) | JSON:Pattern Number | ("true" + Pat Test(
        value = 1;
        1;
    )) | ("false" + Pat Test(
        value = 0;
        1;
    )) | ("null" + Pat Test(
        value = .;
        1;
    )) | ("[" + Pat Test(
        Insert Into( stack, {{}}, 1 );
        value = Empty();
        1;
    )) | ("{" + Pat Test(
        Insert Into( stack, Associative Array(), 1 );
        value = Empty();
        Insert Into( keys, key, 1 );
        1;
    )) | ("]" + Pat Test(
        If( !Is Empty( value ),
            Insert Into( stack[1], value, 1 /*needs reverse later*/ )
        );
        value = stack[1];
        value = Reverse( value );
        Remove From( stack, 1 );
        1;
    )) | ("}" + Pat Test(
        If( !Is Empty( value ),
            stack[1][key] = value
        );
        value = stack[1];
        Remove From( stack, 1 );
        key = keys[1];
        Remove From( keys, 1 );
        1;
    )) | ("," + Pat Test(
        If( Is List( stack[1] ),
            Insert Into( stack[1], value, 1 /*needs reverse later*/ ),
            stack[1][key] = value
        );
        value = Empty();
        1;
    )))
) + Pat R Pos( 0 );
JSON:jsonParse = Function( {json},
    {stack = {}, keys = {}, key = "", value = Empty(), temp = 0, totalLen = Length( json ), failpos = 0},
    If( !Try( Pat Match( 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( 
    
    loadtextfile( "http://api.open-notify.org/iss-now.json?" || Char( Random Integer( 1, 1e6 ) ) )
    //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] = Num( locationData["iss_position"]["latitude"] ); // get the two numbers
        lon[idx] = Num( 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 / 2, height / 2 ),
        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 = N Rows( lon );
        Marker Size( 4 );
        For( i = cur, i >= 1, i--,
            Marker( Combine States( Color State( RGB Color( bright / N Rows( lon ), 0.1, 0.1 ) ), Marker State( 12 ) ), {lon[i], lat[i]} );
            bright--;
            Marker Size( 2 );
        );
        For( i = N Rows( lon ), i > cur, i--,
            Marker( Combine States( Color State( RGB Color( bright / N Rows( lon ), 0.1, 0.1 ) ), Marker State( 12 ) ), {lon[i], lat[i]} );
            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 ) );

 

Last Modified: Apr 10, 2018 9:58 AM