This software is part of a larger project, to build a working Ground Station for Satellite Communication as part of the Amateur Radio Service at UCSC.
The goals of this software are to make satellite radio contacts:
- Point an antenna at an orbiting satellite
- Add Doppler correction to radio communications
- Provide the user with a display of what's going on
We considered many different approaches to building this app before settling on making a Google Chrome Packaged App. High on our list of priorities was cross OS support, and Chrome is available on all major OSs. The Packaged App approach allows us to access USB and COM ports, to manipulate external hardware. Further, a Packaged App is installed on the user's computer, which means it can run completely offline. Finally, the relatively new performance.now()
function gives us microsecond precise timing.
Jason Ragland AG6RM
Team Lead, Antenna Design
Andrew Martino KJ6RFK
Power, Motor, and Mechanical Systems
Sander Middour KK6BSI
Digital Motor Control
Shashwat Kandadai KK6BSH
Tracking Software
Professor Steve Petersen AC6P
Professor John Vesecky AE6TL
Paul Naud
Professor Steve Petersen (AC6P)
- AOS, TCA, LOS calculations, via the Propagation Worker
- Sorted, filterable, collapsible, hide-able, list of sats.
- Tooltips with opentip.
- ThreeJS mousehover detection.
- Geodetic-Normalized and ECI sat paths display.
- Stargazer mode (upwards view of Az/El).
- Displays motors heading.
- Clone this Git repo.
Eventually, the app will be available on the Google Chrome Web Store. For now:
- Install the Google Chrome web browser.
- In Google Chrome, navigate to "chrome://extensions".
- Set your Chrome to "Develeoper Mode", by checking the box.
- Select "Load Unpacked Extension", and choose the 'EarthStation' directory.
Once you have the app running, go to the sidebar on the right and select "Import 3LE File", I've provided one in this repo called "test_sat_tles.txt".
- Angular.js
- satellite.js
- three.js (with a little jquery)
- d3.js
The Tracking Software runs on a Computer, and connects to an external Motor Controller. The Computer sends the Motor Controller aiming directions. The Motor Controller then sends Power to the Motors to move the Antenna. The Computer also connects to the Radio and corrects the transmission frequencies for the Doppler effect.
Ideally, we make use of WebWorkers to offload the mathematically intense logic to separate threads.
The Main Thread (which includes Worker Manager) ONLY deals with initial setting up of the JS environment, and responding to various callbacks. This ensures that it's number one priority remains Graphics Refresh, via the requestAnimationFrame() call. Otherwise, the Main Thread will delay Graphics updates for other logic, the graphics will appear choppy, and the UI will seem non-responsive.
The three.js rendering environment is in WebGL. The Main Thread relays any UI commands that require a change in the WebGL objects (such as zooming, rotating, tracking a satellite).
The Tracking worker is told which satellites to track, and regularly reports those satellites live position. It runs one SGP4 calculation per satellite being tracked, every update cycle, and returns all the live values for each satellite.
The Propagation worker is given a satellite and told to provide several position coordinates in an array, depicting the satellite's path of travel. This thread does many SGP4 calculations for a single satellite over a given span of time. Can also provide this thread with a orbital_resolution value, that increases or decreases the granularity of SGP4 calls.
The localStorage cache is accessed at the start and finish of the app's life-cycle, preserving state across multiple sessions.
AngularJS divides a project into four sections, Controllers, Services, Filters, and Directives. The Controllers and Services are the most important aspects of this particular project.
The Controllers should all be very small, and should be limited to setting up the Model, instantiating the Services, defining callbacks for UI events, and not much else.
This app is a single view, currently run by UICtrl
, which is quite simple. UICtrl
takes in user mouse/keyboard actions and relays them to Services like ThreeJS
and WorkerManager
.
Services are singletons, often used to encapsulate Web APIs, but here they are used to wrap up complex local logic.
ThreeJS
is used to abstract the WebGL world from the AngularJS/HTML/CSS world. UICtrl
detects user actions, such as scrolling, and informs ThreeJS
to appropriately update the 3D objects.
WorkerManager
corresponds exactly to the Worker Manager in the Software Architecture. This Service instantiates Web Workers (threads), sets up their callbacks, and provides the Web Workers with their parameters. Both UICtrl
and ThreeJS
rely on WorkerManager
to provide up-to-date satellite tracking information, for display in HTML and WebGL.
The Radio
and Motor
Services encapsulate the hardware control logic. It does not rely on WebWorkers, because the chrome.serial API is only available from the Main Thread. They both rely on WorkerManager
to provide them with up-to-date tracking information.
Filters provide a modular way to format and present data dynamically in the HTML. We've set up filters to neatly print out coordinates and large numbers.
Directives add functionality to the DOM, allowing us to create new events and capture DOM changes and User actions.
With the goal of keeping the motor control logic as modular as possible, one can add motor control logic to this project with relative ease.
The hardware logic should be a self executing function that returns a set of functions with preset names, as follows.
your_motor_global = (function(){
function stop_motors (connectionId, callback) {
// Do your chrome.serial reads and writes here.
callback(motor_data); // OPTIONAL CALLBACK
}
function move_az_to (connectionId, azimuth, callback) {
// Do your chrome.serial reads and writes here.
callback(motor_data); // OPTIONAL CALLBACK
}
function move_el_to (connectionId, elevation, callback) {
// Do your chrome.serial reads and writes here.
callback(motor_data); // OPTIONAL CALLBACK
}
function get_motor_status (connectionId, callback) {
// Do your chrome.serial reads and writes here.
callback(motor_data); // REQUIRED CALLBACK
}
/* The motor_data callback parameter is of the format:
motor_data = {
azimuth : motor_az,
elevation : motor_el,
status : motor_status
}*/
return {
// return these functions, and the app will operate your motors.
stop_motors : stop_motors,
move_az_to : move_az_to,
move_el_to : move_el_to,
get_status : get_motor_status
};
})();
your_radio_global = (function() {
function get_main_frequency (connectionId, callback) {
// Do your chrome.serial reads and writes here.
callback(radio_main_frequency); // REQUIRED CALLBACK
};
function get_sub_frequency (connectionId, callback) {
// Do your chrome.serial reads and writes here.
callback(radio_sub_frequency); // REQUIRED CALLBACK
};
function set_main_frequency (connectionId, frequency, callback) {
// Do your chrome.serial reads and writes here.
callback(); // OPTIONAL CALLBACK
};
function set_sub_frequency (connectionId, frequency, callback) {
// Do your chrome.serial reads and writes here.
callback(); // OPTIONAL CALLBACK
};
return {
get_main_frequency : get_main_frequency,
get_sub_frequency : get_sub_frequency,
set_main_frequency : set_main_frequency,
set_sub_frequency : set_sub_frequency,
};
})();
This will return the expected data to the rest of the application.
Be sure to update the following part of the Motors or Radios Angular service, in services.js:
// Supported Motors
var supported_motors = {
'Slug Motor' : {
functions : slug_motor, // This is the global variable that the motor control logic should return
bitrate : 57600 // specify the optimal bitrate
},
'Your Custom Motors' : {
functions : your_motor_global,
bitrate : 115200
}
};
// Supported Radios
var supported_radios = {
'ICOM IC821H' : {
functions : icom_821h,
bitrate : 19200
},
'Your Custom Radio' : {
functions : your_radio_global
bitrate : 9600
}
};
as well as index.html, to include the new motor logic:
<!--Loading hardware control libraries !-->
<script src="lib/motors/slug_motor.js"></script>
<script src="lib/motors/your_motor.js"></script>
<script src="lib/radios/icom_821h.js"></script>
<script src="lib/radios/your_radio.js"></script>
MIT License, co-owned by Shashwat Kandadai and UCSC, 2013. The License applies to all code in here that isn't already licensed.