*) Monitor-idle mode
- *) Frequency scanning
+ *) TeleBT battery voltage
- *) Select satellite imaging mode
+ *) Select tracker by clicking map
- *) TeleBT battery voltage
+ *) Auto select tracker after long delay
- *) Deal with long bluetooth list. Currently, a list longer than
- the screen makes it impossible to use entries off the bottom.
+ *) Evaluate performance issues
- *) Pickle/unpickle state instead of reloading entire history from
- file. Current restart time is lengthy.
+ *) Merge offline/online maps into single tab with mode
+
+ *) Make voice responses depend on selected tab
+
+ *) Monitor TeleMega igniters
+
+ *) Convert to four tab design:
+
+ 1) Pad
+
+ Report out GPS status, report igniter/battery status changes
+
+ 2) Flight
+
+ Report height, speed, az/el/range
+
+ 3) Recovery
+
+ Report range bearing/heading (bearing if stationary,
+ heading if in motion
+
+ 4) Map
+
+ Pick out report based on current flight status
+ (presume flight if in motion, recovery otherwise)
Completed features
Done
+ *) Select satellite imaging mode
+
+ Done
+
+ *) Deal with long bluetooth list. Currently, a list longer than
+ the screen makes it impossible to use entries off the bottom.
+
+ Done
+
+ *) Pickle/unpickle state instead of reloading entire history from
+ file. Current restart time is lengthy.
+
+ Done
+
+ *) Offline maps
+
+ Done
+
+ *) Multi-tracker management
+
+ Done
+
+ *) Provide units for age field, turn red if old
+
+ Done
<item android:id="@+id/disconnect"
android:icon="@android:drawable/ic_notification_clear_all"
android:title="@string/disconnect_device" />
- <item android:id="@+id/quit"
- android:icon="@android:drawable/ic_menu_close_clear_cancel"
- android:title="@string/quit" />
<item android:id="@+id/select_freq"
android:icon="@android:drawable/ic_menu_preferences"
android:title="@string/select_freq" />
<item android:id="@+id/map_type"
android:icon="@android:drawable/ic_menu_mapmode"
android:title="@string/map_type" />
+ <item android:id="@+id/select_tracker"
+ android:icon="@android:drawable/ic_menu_view"
+ android:title="@string/select_tracker"/>
+ <item android:id="@+id/delete_track"
+ android:icon="@android:drawable/ic_notification_clear_all"
+ android:title="@string/delete_track"/>
+ <item android:id="@+id/quit"
+ android:icon="@android:drawable/ic_menu_close_clear_cancel"
+ android:title="@string/quit" />
</menu>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<resources>
+ <color name="old_color">#ffff4040</color>
+</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="CustomTheme" parent="android:Theme">
- <item name="android:windowNoTitle">false</item>
+ <style name="CustomTheme" parent="android:Theme.Holo">
</style>
</resources>
<string name="select_rate">Select data rate</string>
<string name="change_units">Change units</string>
<string name="preload_maps">Load Maps</string>
+ <string name="select_tracker">Select Tracker</string>
+ <string name="delete_track">Delete Track</string>
<string name="map_type">Map Type</string>
<!-- MapTypeActivity -->
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.util.DisplayMetrics;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.Window;
-import android.view.View;
-import android.view.LayoutInflater;
-import android.widget.TabHost;
-import android.widget.TextView;
-import android.widget.RelativeLayout;
-import android.widget.Toast;
+import android.view.*;
+import android.widget.*;
import android.app.AlertDialog;
import android.location.Location;
import android.hardware.usb.*;
+import android.graphics.*;
+import android.graphics.drawable.*;
import org.altusmetrum.altoslib_7.*;
private BluetoothAdapter mBluetoothAdapter = null;
- // Layout Views
- private TextView mTitle;
-
// Flight state values
private TextView mCallsignView;
private TextView mRSSIView;
private RelativeLayout mStateLayout;
private TextView mStateView;
private TextView mAgeView;
+ private boolean mAgeViewOld;
+ private int mAgeNewColor;
+ private int mAgeOldColor;
// field to display the version at the bottom of the screen
private TextView mVersion;
// Timer and Saved flight state for Age calculation
private Timer timer;
AltosState saved_state;
+ TelemetryState telemetry_state;
+ Integer[] serials;
UsbDevice pending_usb_device;
boolean start_with_usb;
switch (msg.what) {
case MSG_STATE:
AltosDebug.debug("MSG_STATE");
- TelemetryState telemetry_state = (TelemetryState) msg.obj;
- if (telemetry_state == null) {
+ if (msg.obj == null) {
AltosDebug.debug("telemetry_state null!");
return;
}
-
- ad.update_state(telemetry_state);
+ ad.update_state((TelemetryState) msg.obj);
break;
case MSG_UPDATE_AGE:
AltosDebug.debug("MSG_UPDATE_AGE");
if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400)
str = str.concat(String.format(" %d bps",
AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate]));
- mTitle.setText(str);
+ setTitle(str);
} else {
- mTitle.setText(R.string.title_connected_to);
+ setTitle(R.string.title_connected_to);
}
break;
case TelemetryState.CONNECT_CONNECTING:
if (telemetry_state.address != null)
- mTitle.setText(String.format("Connecting to %s...", telemetry_state.address.name));
+ setTitle(String.format("Connecting to %s...", telemetry_state.address.name));
else
- mTitle.setText("Connecting to something...");
+ setTitle("Connecting to something...");
break;
case TelemetryState.CONNECT_DISCONNECTED:
case TelemetryState.CONNECT_NONE:
- mTitle.setText(R.string.title_not_connected);
+ setTitle(R.string.title_not_connected);
break;
}
}
boolean registered_units_listener;
- void update_state(TelemetryState telemetry_state) {
+ int current_serial;
+
+ void update_state(TelemetryState new_telemetry_state) {
+
+ if (new_telemetry_state != null)
+ telemetry_state = new_telemetry_state;
+
+ if (current_serial == 0)
+ current_serial = telemetry_state.latest_serial;
if (!registered_units_listener) {
registered_units_listener = true;
AltosPreferences.register_units_listener(this);
}
+ serials = telemetry_state.states.keySet().toArray(new Integer[0]);
+
update_title(telemetry_state);
- update_ui(telemetry_state.state, telemetry_state.location);
- if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED)
- start_timer();
+
+ AltosDebug.debug("update state current serial %d\n", current_serial);
+
+ AltosState state = null;
+ if (telemetry_state.states.containsKey(current_serial))
+ state = telemetry_state.states.get(current_serial);
+
+ update_ui(telemetry_state, state, telemetry_state.location);
+
+ start_timer();
}
boolean same_string(String a, String b) {
}
}
+
+ private int blend_component(int a, int b, double r, int shift, int mask) {
+ return ((int) (((a >> shift) & mask) * r + ((b >> shift) & mask) * (1 - r)) & mask) << shift;
+ }
+ private int blend_color(int a, int b, double r) {
+ return (blend_component(a, b, r, 0, 0xff) |
+ blend_component(a, b, r, 8, 0xff) |
+ blend_component(a, b, r, 16, 0xff) |
+ blend_component(a, b, r, 24, 0xff));
+ }
+
+ int state_age(AltosState state) {
+ return (int) ((System.currentTimeMillis() - state.received_time + 500) / 1000);
+ }
+
+ void set_screen_on(int age) {
+ if (age < 60)
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ else
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
void update_age() {
- if (saved_state != null)
- mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000));
+ if (saved_state != null) {
+ int age = state_age(saved_state);
+
+ double age_scale = age / 100.0;
+
+ if (age_scale > 1.0)
+ age_scale = 1.0;
+
+ mAgeView.setTextColor(blend_color(mAgeOldColor, mAgeNewColor, age_scale));
+
+ set_screen_on(age);
+
+ String text;
+ if (age < 60)
+ text = String.format("%ds", age);
+ else if (age < 60 * 60)
+ text = String.format("%dm", age / 60);
+ else if (age < 60 * 60 * 24)
+ text = String.format("%dh", age / (60 * 60));
+ else
+ text = String.format("%dd", age / (24 * 60 * 60));
+ mAgeView.setText(text);
+ }
}
- void update_ui(AltosState state, Location location) {
+ void update_ui(TelemetryState telem_state, AltosState state, Location location) {
int prev_state = AltosLib.ao_flight_invalid;
prev_state = saved_state.state;
if (state != null) {
+ set_screen_on(state_age(state));
+
if (state.state == AltosLib.ao_flight_stateless) {
boolean prev_locked = false;
boolean locked = false;
}
for (AltosDroidTab mTab : mTabs)
- mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem());
+ mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem());
if (state != null && mAltosVoice != null)
mAltosVoice.tell(state, from_receiver);
fm = getSupportFragmentManager();
// Set up the window layout
- requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.altosdroid);
- getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
// Create the Tabs and ViewPager
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator(create_tab_view("Map")), TabMap.class, null);
mTabsAdapter.addTab(mTabHost.newTabSpec("offmap").setIndicator(create_tab_view("OffMap")), TabMapOffline.class, null);
- // Set up the custom title
- mTitle = (TextView) findViewById(R.id.title_left_text);
- mTitle.setText(R.string.app_name);
- mTitle = (TextView) findViewById(R.id.title_right_text);
-
// Display the Version
mVersion = (TextView) findViewById(R.id.version);
mVersion.setText("Version: " + BuildInfo.version +
mStateLayout = (RelativeLayout) findViewById(R.id.state_container);
mStateView = (TextView) findViewById(R.id.state_value);
mAgeView = (TextView) findViewById(R.id.age_value);
+ mAgeNewColor = mAgeView.getTextColors().getDefaultColor();
+ mAgeOldColor = getResources().getColor(R.color.old_color);
}
private boolean ensureBluetooth() {
}
}
+ void select_tracker(int serial) {
+ int i;
+ for (i = 0; i < serials.length; i++)
+ if (serials[i] == serial)
+ break;
+ if (i == serials.length)
+ return;
+
+ AltosDebug.debug("Switching to serial %d\n", serial);
+ current_serial = serial;
+ update_state(null);
+ }
+
+ void delete_track(int serial) {
+ try {
+ mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial));
+ } catch (Exception ex) {
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent serverIntent = null;
serverIntent = new Intent(this, MapTypeActivity.class);
startActivityForResult(serverIntent, REQUEST_MAP_TYPE);
return true;
+ case R.id.select_tracker:
+ if (serials != null) {
+ String[] trackers = new String[serials.length];
+ for (int i = 0; i < serials.length; i++)
+ trackers[i] = String.format("%d", serials[i]);
+ AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+ builder_serial.setTitle("Select a tracker");
+ builder_serial.setItems(trackers,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ select_tracker(serials[item]);
+ }
+ });
+ AlertDialog alert_serial = builder_serial.create();
+ alert_serial.show();
+
+ }
+ return true;
+ case R.id.delete_track:
+ if (serials != null) {
+ String[] trackers = new String[serials.length];
+ for (int i = 0; i < serials.length; i++)
+ trackers[i] = String.format("%d", serials[i]);
+ AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+ builder_serial.setTitle("Delete a track");
+ builder_serial.setItems(trackers,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ delete_track(serials[item]);
+ }
+ });
+ AlertDialog alert_serial = builder_serial.create();
+ alert_serial.show();
+
+ }
+ return true;
}
return false;
}
-
}
public String[] keys() {
Map<String, ?> all = prefs.getAll();
- return (String[])all.keySet().toArray();
+ Object[] ao = all.keySet().toArray();
+
+ String[] as = new String[ao.length];
+ for (int i = 0; i < ao.length; i++)
+ as[i] = (String) ao[i];
+ return as;
}
public AltosPreferencesBackend node(String key) {
}
public void remove(String key) {
+ AltosDebug.debug("remove preference %s\n", key);
editor.remove(key);
}
import android.widget.TextView;
public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
+ TelemetryState last_telem_state;
AltosState last_state;
AltosGreatCircle last_from_receiver;
Location last_receiver;
+ AltosDroid altos_droid;
- public abstract void show(AltosState state, AltosGreatCircle from_receiver, Location receiver);
+ public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
public abstract String tab_name();
}
public void units_changed(boolean imperial_units) {
- if (!isHidden() && last_state != null)
- show(last_state, last_from_receiver, last_receiver);
+ if (!isHidden())
+ show(last_telem_state, last_state, last_from_receiver, last_receiver);
}
public void set_value(TextView text_view,
FragmentTransaction ft = AltosDroid.fm.beginTransaction();
AltosDebug.debug("set visible %b %s\n", visible, tab_name());
if (visible) {
- AltosState state = last_state;
- AltosGreatCircle from_receiver = last_from_receiver;
- Location receiver = last_receiver;
-
ft.show(this);
- show(state, from_receiver, receiver);
+ show(last_telem_state, last_state, last_from_receiver, last_receiver);
} else
ft.hide(this);
ft.commitAllowingStateLoss();
}
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ altos_droid = (AltosDroid) activity;
+ altos_droid.registerTab(this);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ altos_droid.unregisterTab(this);
+ altos_droid = null;
+ }
+
@Override
public void onResume() {
super.onResume();
set_visible(true);
}
- public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) {
+ public void update_ui(TelemetryState telem_state, AltosState state,
+ AltosGreatCircle from_receiver, Location receiver, boolean is_current)
+ {
+ last_telem_state = telem_state;
last_state = state;
last_from_receiver = from_receiver;
last_receiver = receiver;
+ AltosDebug.debug("update_ui tab %s is_current %b\n", tab_name(), is_current);
if (is_current)
- show(state, from_receiver, receiver);
+ show(telem_state, state, from_receiver, receiver);
else
return;
}
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
- // Get the BluetoothDevice object from the Intent
+
+ /* Get the BluetoothDevice object from the Intent
+ */
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- // If it's already paired, skip it, because it's been listed already
- if ( device.getBondState() != BluetoothDevice.BOND_BONDED
- && device.getName().startsWith("TeleBT") ) {
- mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+
+ /* If it's already paired, skip it, because it's been listed already
+ */
+ if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED)
+ {
+ String name = device.getName();
+ if (name != null && name.startsWith("TeleBT"))
+ mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
- // When discovery is finished, change the Activity title
+
+ /* When discovery is finished, change the Activity title
+ */
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
import android.location.Location;
public class TabAscent extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
private TextView mHeightView;
private TextView mMaxHeightView;
private TextView mSpeedView;
private TextView mMainVoltageView;
private GoNoGoLights mMainLights;
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tab_ascent, container, false);
return v;
}
- @Override
- public void onDestroy() {
- super.onDestroy();
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
- }
-
public String tab_name() {
return "ascent";
}
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (state != null) {
set_value(mHeightView, AltosConvert.height, 6, state.height());
set_value(mHeightView, AltosConvert.height, 6, state.height());
import android.location.Location;
public class TabDescent extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
private TextView mSpeedView;
private TextView mHeightView;
private TextView mElevationView;
private TextView mMainVoltageView;
private GoNoGoLights mMainLights;
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tab_descent, container, false);
return v;
}
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
- }
-
public String tab_name() { return "descent"; }
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (state != null) {
set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
set_value(mHeightView, AltosConvert.height, 6, state.height());
import android.location.Location;
public class TabLanded extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
private TextView mBearingView;
private TextView mDistanceView;
private TextView mTargetLatitudeView;
private TextView mMaxSpeedView;
private TextView mMaxAccelView;
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tab_landed, container, false);
return v;
}
- @Override
- public void onDestroy() {
- super.onDestroy();
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
- }
-
public String tab_name() { return "landed"; }
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (from_receiver != null) {
mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
import android.app.Activity;
import android.graphics.Color;
+import android.graphics.*;
import android.os.Bundle;
import android.support.v4.app.Fragment;
//import android.support.v4.app.FragmentTransaction;
import android.location.Location;
public class TabMap extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
private SupportMapFragment mMapFragment;
private GoogleMap mMap;
private boolean mapLoaded = false;
private double mapAccuracy = -1;
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
+ private Bitmap rocket_bitmap(String text) {
+
+ /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
+ */
+ Bitmap orig_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
+ Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true);
+
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setTextSize(40);
+ paint.setColor(0xff000000);
+
+ Rect bounds = new Rect();
+ paint.getTextBounds(text, 0, text.length(), bounds);
+
+ int width = bounds.right - bounds.left;
+ int height = bounds.bottom - bounds.top;
+
+ float x = bitmap.getWidth() / 2.0f - width / 2.0f;
+ float y = bitmap.getHeight() / 2.0f - height / 2.0f;
+
+ AltosDebug.debug("map label x %f y %f\n", x, y);
+
+ canvas.drawText(text, 0, text.length(), x, y, paint);
+ return bitmap;
+ }
+
+ private Marker rocket_marker(int serial, double lat, double lon) {
+ Bitmap bitmap = rocket_bitmap(String.format("%d", serial));
+
+ return mMap.addMarker(new MarkerOptions()
+ .icon(BitmapDescriptorFactory.fromBitmap(bitmap))
+ .position(new LatLng(lat, lon))
+ .visible(false));
}
@Override
getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit();
}
- @Override
- public void onDestroyView() {
- super.onDestroyView();
-
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
-
- //Fragment fragment = (getFragmentManager().findFragmentById(R.id.map));
- //FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
- //ft.remove(fragment);
- //ft.commit();
- }
-
private void setupMap() {
mMap = mMapFragment.getMap();
if (mMap != null) {
- set_map_type(mAltosDroid.map_type);
+ set_map_type(altos_droid.map_type);
mMap.setMyLocationEnabled(true);
mMap.getUiSettings().setTiltGesturesEnabled(false);
mMap.getUiSettings().setZoomControlsEnabled(false);
- mRocketMarker = mMap.addMarker(
- // From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
- new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.rocket))
- .position(new LatLng(0,0))
- .visible(false)
- );
+ Bitmap label_bitmap = rocket_bitmap("hello");
+
+ mRocketMarker = rocket_marker(1800,0,0);
mPadMarker = mMap.addMarker(
new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
public String tab_name() { return "map"; }
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (from_receiver != null) {
mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
if (mapLoaded) {
if (state.gps != null) {
mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon));
+ mRocketMarker.setTitle("hello world");
+ mRocketMarker.setSnippet("hello");
mRocketMarker.setVisible(true);
mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon)));
public class TabMapOffline extends AltosDroidTab implements AltosMapInterface {
- AltosDroid mAltosDroid;
-
AltosMap map;
AltosLatLon here;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
map = new AltosMap(this);
- map.set_maptype(mAltosDroid.map_type);
+ map.set_maptype(altos_droid.map_type);
pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
/* arrow at the bottom of the launchpad image */
here_off_y = here_bitmap.getHeight() / 2;
}
- @Override
- public void onDetach() {
- super.onDetach();
- mAltosDroid = null;
- }
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public void onDestroyView() {
super.onDestroyView();
- mAltosDroid.unregisterTab(this);
}
private void center(double lat, double lon, double accuracy) {
public String tab_name() { return "offmap"; }
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (from_receiver != null) {
mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
}
if (state.pad_lat != AltosLib.MISSING && pad == null)
pad = new AltosLatLon(state.pad_lat, state.pad_lon);
+ }
- int serial = state.serial;
- if (serial == AltosLib.MISSING)
- serial = 0;
+ if (telem_state != null) {
+ Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
+ Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]);
- Rocket rocket = null;
+ /* remove deleted keys */
+ for (int serial : old_serial) {
+ if (!telem_state.states.containsKey(serial))
+ rockets.remove(serial);
+ }
+
+ /* set remaining keys */
- if (state.gps != null && state.gps.locked) {
- if (!rockets.containsKey(serial)) {
+ for (int serial : new_serial) {
+ Rocket rocket;
+ AltosState t_state = telem_state.states.get(serial);
+ if (rockets.containsKey(serial))
+ rocket = rockets.get(serial);
+ else {
rocket = new Rocket(String.format("%d", serial), this);
rockets.put(serial, rocket);
- } else
- rocket = rockets.get(serial);
- rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon));
+ }
+ rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon));
}
}
import android.location.Location;
public class TabPad extends AltosDroidTab {
- AltosDroid mAltosDroid;
-
private TextView mBatteryVoltageView;
private TextView mBatteryVoltageLabel;
private GoNoGoLights mBatteryLights;
private TextView mPadLongitudeView;
private TextView mPadAltitudeView;
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mAltosDroid = (AltosDroid) activity;
- mAltosDroid.registerTab(this);
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tab_pad, container, false);
return v;
}
- @Override
- public void onDestroy() {
- super.onDestroy();
- mAltosDroid.unregisterTab(this);
- mAltosDroid = null;
- }
-
public String tab_name() { return "pad"; }
- public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (state != null) {
mBatteryVoltageView.setText(AltosDroid.number("%4.2f V", state.battery_voltage));
mBatteryLights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
logger = null;
}
}
-
+
void handleExternalStorageState() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
import java.text.*;
import java.io.*;
+import java.util.*;
import java.util.concurrent.*;
import android.os.Handler;
Handler handler;
AltosLink link;
- AltosState state = null;
LinkedBlockingQueue<AltosLine> telemQueue;
- public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException {
+ public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException {
AltosLine l = telemQueue.take();
if (l.line == null)
throw new IOException("IO error");
AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
- if (state == null)
- state = new AltosState();
- else
- state = state.clone();
- telem.update_state(state);
- return state;
+ return telem;
}
public void close() {
- state = null;
link.remove_monitor(telemQueue);
link = null;
telemQueue.clear();
}
public void run() {
- AltosState state = null;
-
try {
AltosDebug.debug("starting loop");
while (telemQueue != null) {
try {
- state = read();
- handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();
+ AltosTelemetry telem = read();
+ handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget();
} catch (ParseException pp) {
AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());
} catch (AltosCRCException ce) {
}
}
- public TelemetryReader (AltosLink in_link, Handler in_handler, AltosState in_state) {
+ public TelemetryReader (AltosLink in_link, Handler in_handler) {
AltosDebug.debug("connected TelemetryReader create started");
link = in_link;
handler = in_handler;
- state = in_state;
telemQueue = new LinkedBlockingQueue<AltosLine>();
link.add_monitor(telemQueue);
link.set_telemetry(AltosLib.ao_telemetry_standard);
package org.altusmetrum.AltosDroid;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.concurrent.TimeoutException;
-import java.util.Timer;
-import java.util.TimerTask;
+import java.util.*;
import android.app.Notification;
//import android.app.NotificationManager;
static final int MSG_CRC_ERROR = 10;
static final int MSG_SETBAUD = 11;
static final int MSG_DISCONNECT = 12;
+ static final int MSG_DELETE_SERIAL = 13;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
s.address = null;
s.disconnect(true);
break;
+ case MSG_DELETE_SERIAL:
+ AltosDebug.debug("Delete Serial command received");
+ s.delete_serial((Integer) msg.obj);
+ break;
case MSG_SETFREQUENCY:
AltosDebug.debug("MSG_SETFREQUENCY");
s.telemetry_state.frequency = (Double) msg.obj;
* Messages from TelemetryReader
*/
case MSG_TELEMETRY:
- s.telemetry_state.state = (AltosState) msg.obj;
- if (s.telemetry_state.state != null) {
- AltosDebug.debug("Save state");
- AltosPreferences.set_state(0, s.telemetry_state.state, null);
- }
AltosDebug.debug("MSG_TELEMETRY");
- s.send_to_clients();
+ s.telemetry((AltosTelemetry) msg.obj);
break;
case MSG_CRC_ERROR:
// forward crc error messages
}
}
+ /* Handle telemetry packet
+ */
+ private void telemetry(AltosTelemetry telem) {
+ AltosState state;
+
+ if (telemetry_state.states.containsKey(telem.serial))
+ state = telemetry_state.states.get(telem.serial).clone();
+ else
+ state = new AltosState();
+ telem.update_state(state);
+ telemetry_state.states.put(telem.serial, state);
+ if (state != null) {
+ AltosDebug.debug("Save state %d", telem.serial);
+ AltosPreferences.set_state(telem.serial, state, null);
+ }
+ send_to_clients();
+ }
+
/* Construct the message to deliver to clients
*/
private Message message() {
if (telemetry_state == null)
AltosDebug.debug("telemetry_state null!");
- if (telemetry_state.state == null)
- AltosDebug.debug("telemetry_state.state null!");
+ if (telemetry_state.states == null)
+ AltosDebug.debug("telemetry_state.states null!");
return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
}
}
}
+ private void delete_serial(int serial) {
+ telemetry_state.states.remove((Integer) serial);
+ AltosPreferences.remove_state(serial);
+ send_to_clients();
+ }
+
private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
// Get the BLuetoothDevice object
BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
telemetry_state.address = address;
- telemetry_reader = new TelemetryReader(altos_link, handler, telemetry_state.state);
+ telemetry_reader = new TelemetryReader(altos_link, handler);
telemetry_reader.start();
AltosDebug.debug("connected TelemetryReader started");
telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
telemetry_state.address = null;
- AltosSavedState saved_state = AltosPreferences.state(0);
+ /* Pull the saved state information out of the preferences database
+ */
+ ArrayList<Integer> serials = AltosPreferences.list_states();
- if (saved_state != null) {
- AltosDebug.debug("recovered old state flight %d\n", saved_state.state.flight);
- telemetry_state.state = saved_state.state;
+ telemetry_state.latest_serial = AltosPreferences.latest_state();
+
+ for (int serial : serials) {
+ AltosSavedState saved_state = AltosPreferences.state(serial);
+ if (saved_state != null) {
+ if (serial == 0) {
+ serial = saved_state.state.serial;
+ AltosPreferences.set_state(serial, saved_state.state, saved_state.listener_state);
+ AltosPreferences.remove_state(0);
+ }
+ if (telemetry_state.latest_serial == 0)
+ telemetry_state.latest_serial = serial;
+
+ AltosDebug.debug("recovered old state serial %d flight %d\n",
+ serial,
+ saved_state.state.flight);
+ if (saved_state.state.gps != null)
+ AltosDebug.debug("\tposition %f,%f\n",
+ saved_state.state.gps.lat,
+ saved_state.state.gps.lon);
+ telemetry_state.states.put(serial, saved_state.state);
+ }
}
// Listen for GPS and Network position updates
package org.altusmetrum.AltosDroid;
+import java.util.*;
import org.altusmetrum.altoslib_7.*;
import android.location.Location;
int connect;
DeviceAddress address;
AltosConfigData config;
- AltosState state;
Location location;
int crc_errors;
double frequency;
int telemetry_rate;
+ HashMap<Integer,AltosState> states;
+
+ int latest_serial;
+
public TelemetryState() {
connect = CONNECT_NONE;
config = null;
- state = null;
+ states = new HashMap<Integer,AltosState>();
location = null;
crc_errors = 0;
frequency = AltosPreferences.frequency(0);
import java.io.*;
import java.util.*;
+import java.text.*;
public class AltosPreferences {
public static AltosPreferencesBackend backend = null;
public final static String logfilePreferenceFormat = "LOGFILE-%d";
/* state preference name */
+ public final static String statePreferenceHead = "STATE-";
public final static String statePreferenceFormat = "STATE-%d";
+ public final static String statePreferenceLatest = "STATE-LATEST";
/* voice preference name */
public final static String voicePreference = "VOICE";
synchronized(backend) {
backend.putBytes(String.format(statePreferenceFormat, serial), bytes);
+ backend.putInt(statePreferenceLatest, serial);
flush_preferences();
}
} catch (IOException ie) {
}
}
+ public static ArrayList<Integer> list_states() {
+ String[] keys = backend.keys();
+ ArrayList<Integer> states = new ArrayList<Integer>();
+
+ for (String key : keys) {
+ if (key.startsWith(statePreferenceHead)) {
+ try {
+ int serial = AltosParse.parse_int(key.substring(statePreferenceHead.length()));
+ states.add(serial);
+ } catch (ParseException pe) {
+ }
+ }
+ }
+ return states;
+ }
+
+ public static void remove_state(int serial) {
+ synchronized(backend) {
+ backend.remove(String.format(statePreferenceFormat, serial));
+ }
+ }
+
+ public static int latest_state() {
+ int latest = 0;
+ synchronized (backend) {
+ latest = backend.getInt(statePreferenceLatest, 0);
+ }
+ return latest;
+ }
+
public static AltosSavedState state(int serial) {
byte[] bytes = null;
;;
esac
-while true; do
- echo 'C 1' > $dev
-
- echo -n "Generating RF carrier. Please enter measured frequency [enter for done]: "
-
- read FREQ
-
- echo 'C 0' > $dev
-
+../ao-tools/ao-cal-freq/ao-cal-freq --dev=$dev
+case $? in
+ 0)
calline=`./get-radio-cal $dev`
- CURRENT_CAL=`echo $calline | awk '{print $2}'`
+ CAL_VALUE=`echo $calline | awk '{print $2}'`
CURRENT_FREQ=`echo $calline | awk '{print $4}'`
- CAL_VALUE=$CURRENT_CAL
-
- case "$FREQ" in
- "")
- echo $SERIAL","$CAL_VALUE >> cal_values
- exit 0
- ;;
- *)
- echo "Current radio calibration "$CURRENT_CAL
- echo "Current radio frequency "$CURRENT_FREQ
-
- CAL_VALUE=`nickle -e "floor($CURRENT_FREQ / $FREQ * $CURRENT_CAL + 0.5)"`
-
- echo "Programming flash with cal value " $CAL_VALUE
-
- dd if=$dev iflag=nonblock
-
- cat << EOF > $dev
-c f $CAL_VALUE
-c w
-EOF
-
- echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE
- ;;
- esac
-done
-
+ echo $SERIAL","$CAL_VALUE >> cal_values
+ exit 0
+ ;;
+ *)
+ exit 1
+ ;;
+esac
;;
esac
-echo 'E 0' > $dev
+SERIAL=$SERIAL ./cal-freq $dev
./test-telegps
-SERIAL=$SERIAL ./cal-freq $dev
-
exit $?
ao-load ao-telem ao-send-telem ao-sky-flash \
ao-dumpflash ao-edit-telem ao-dump-up ao-elftohex \
ao-flash ao-usbload ao-test-igniter ao-test-baro \
- ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng
+ ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng \
+ ao-cal-freq
if LIBSTLINK
SUBDIRS += ao-stmload
endif
--- /dev/null
+ao-cal-freq
--- /dev/null
+bin_PROGRAMS=ao-cal-freq
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+
+ao_cal_freq_DEPENDENCIES = $(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_cal_freq_LDADD=$(top_builddir)/ao-tools/lib/libao-tools.a $(LIBUSB_LIBS) -lm
+
+ao_cal_freq_SOURCES=ao-cal-freq.c
+
+man_MANS = ao-cal-freq.1
--- /dev/null
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-LOAD 1 "ao-cal-freq" ""
+.SH NAME
+ao-cal-freq \- Calibrate AltOS flight computer frequency
+.SH SYNOPSIS
+.B "ao-cal-freq"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+.SH DESCRIPTION
+.I ao-cal-freq
+drives the frequency calibration process and saves the result.
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device the debugger uses to communicate with
+the target device. The special name 'BITBANG' directs ao-dbg to use
+the cp2103 connection, otherwise this should be a usb serial port
+connected to a suitable cc1111 debug node.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleMega:2
+.br
+TeleMega
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
+.SH USAGE
+.I ao-cal-freq
+opens the target device, interactively calibrates the frequency, then
+shows the resulting calibration values and saves them to configuration
+memory.
+.SH AUTHOR
+Keith Packard
--- /dev/null
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <math.h>
+#include "ao-elf.h"
+#include "ccdbg.h"
+#include "cc-usb.h"
+#include "cc.h"
+#include "ao-verbose.h"
+
+static const struct option options[] = {
+ { .name = "tty", .has_arg = 1, .val = 'T' },
+ { .name = "device", .has_arg = 1, .val = 'D' },
+ { .name = "raw", .has_arg = 0, .val = 'r' },
+ { .name = "verbose", .has_arg = 1, .val = 'v' },
+ { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+ fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>]\n", program);
+ exit(1);
+}
+
+void
+done(struct cc_usb *cc, int code)
+{
+ cc_usb_close(cc);
+ exit (code);
+}
+
+static int
+ends_with(char *whole, char *suffix)
+{
+ int whole_len = strlen(whole);
+ int suffix_len = strlen(suffix);
+
+ if (suffix_len > whole_len)
+ return 0;
+ return strcmp(whole + whole_len - suffix_len, suffix) == 0;
+}
+
+static int
+starts_with(char *whole, char *prefix)
+{
+ int whole_len = strlen(whole);
+ int prefix_len = strlen(prefix);
+
+ if (prefix_len > whole_len)
+ return 0;
+ return strncmp(whole, prefix, prefix_len) == 0;
+}
+
+static char **
+tok(char *line) {
+ char **strs = malloc (sizeof (char *)), *str;
+ int n = 0;
+
+ while ((str = strtok(line, " \t"))) {
+ line = NULL;
+ strs = realloc(strs, (n + 2) * sizeof (char *));
+ strs[n] = strdup(str);
+ n++;
+ }
+ strs[n] = '\0';
+ return strs;
+}
+
+static void
+free_strs(char **strs) {
+ char *str;
+ int i;
+
+ for (i = 0; (str = strs[i]) != NULL; i++)
+ free(str);
+ free(strs);
+}
+
+struct flash {
+ struct flash *next;
+ char line[512];
+ char **strs;
+};
+
+static struct flash *
+flash(struct cc_usb *usb)
+{
+ struct flash *head = NULL, **tail = &head;
+ cc_usb_printf(usb, "c s\nv\n");
+ for (;;) {
+ char line[512];
+ struct flash *b;
+
+ cc_usb_getline(usb, line, sizeof (line));
+ b = malloc (sizeof (struct flash));
+ strcpy(b->line, line);
+ b->strs = tok(line);
+ b->next = NULL;
+ *tail = b;
+ tail = &b->next;
+ if (strstr(line, "software-version"))
+ break;
+ }
+ return head;
+}
+
+static void
+free_flash(struct flash *b) {
+ struct flash *n;
+
+ while (b) {
+ n = b->next;
+ free_strs(b->strs);
+ free(b);
+ b = n;
+ }
+}
+
+char **
+find_flash(struct flash *b, char *word0) {
+ int i;
+ for (;b; b = b->next) {
+ if (strstr(b->line, word0))
+ return b->strs;
+ }
+ return NULL;
+}
+
+void
+await_key(void)
+{
+ struct termios termios, termios_save;
+ char buf[512];
+
+ tcgetattr(0, &termios);
+ termios_save = termios;
+ cfmakeraw(&termios);
+ tcsetattr(0, TCSAFLUSH, &termios);
+ read(0, buf, sizeof (buf));
+ tcsetattr(0, TCSAFLUSH, &termios_save);
+}
+
+int
+do_cal(struct cc_usb *usb) {
+ struct flash *b;
+ char line[1024];
+ double measured_freq;
+ char **cur_freq_words;
+ char **cur_cal_words;
+ char *line_end;
+ int cur_freq;
+ int cur_cal;
+ int new_cal;
+
+ cc_usb_printf(usb, "E 0\n");
+
+ for(;;) {
+ cc_usb_printf(usb, "C 1\n");
+ cc_usb_sync(usb);
+
+ printf("Generating RF carrier. Please enter measured frequency [enter for done]: ");
+ fflush(stdout);
+ fgets(line, sizeof (line) - 1, stdin);
+ cc_usb_printf(usb, "C 0\n");
+ cc_usb_sync(usb);
+
+ measured_freq = strtod(line, &line_end);
+ if (line_end == line)
+ break;
+
+ b = flash(usb);
+
+ cur_cal_words = find_flash(b, "Radio cal:");
+ cur_freq_words = find_flash(b, "Frequency:");
+
+ if (!cur_cal_words || !cur_freq_words) {
+ printf("no response\n");
+ return 0;
+ }
+
+ cur_cal = atoi(cur_cal_words[2]);
+ cur_freq = atoi(cur_freq_words[1]);
+
+ printf ("Current radio calibration %d\n", cur_cal);
+ printf ("Current radio frequency: %d\n", cur_freq);
+
+
+ new_cal = floor ((((double) cur_freq / 1000.0) / measured_freq) * cur_cal + 0.5);
+
+ printf ("Programming flash with cal value %d\n", new_cal);
+
+ cc_usb_printf (usb, "c f %d\nc w\n", new_cal);
+ cc_usb_sync(usb);
+ }
+ return 1;
+}
+
+int
+main (int argc, char **argv)
+{
+ char *device = NULL;
+ char *filename;
+ Elf *e;
+ unsigned int s;
+ int i;
+ int c;
+ int tries;
+ struct cc_usb *cc = NULL;
+ char *tty = NULL;
+ int success;
+ int verbose = 0;
+ int ret = 0;
+ int expected_size;
+
+ while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) {
+ switch (c) {
+ case 'T':
+ tty = optarg;
+ break;
+ case 'D':
+ device = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ ao_verbose = verbose;
+
+ if (verbose > 1)
+ ccdbg_add_debug(CC_DEBUG_BITBANG);
+
+ if (!tty)
+ tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
+ if (!tty)
+ tty = cc_usbdevs_find_by_arg(device, "TeleMega");
+ if (!tty)
+ tty = getenv("ALTOS_TTY");
+ if (!tty)
+ tty="/dev/ttyACM0";
+
+ cc = cc_usb_open(tty);
+
+ if (!cc)
+ exit(1);
+
+ if (!do_cal(cc))
+ ret = 1;
+ done(cc, ret);
+}
ao-tools/ao-test-baro/Makefile
ao-tools/ao-test-flash/Makefile
ao-tools/ao-cal-accel/Makefile
+ao-tools/ao-cal-freq/Makefile
ao-tools/ao-test-gps/Makefile
ao-tools/ao-usbtrng/Makefile
ao-utils/Makefile