*) Highlight current frequency in the frequency list.
+ Placed current frequency in title bar
+
*) Random frequency selection. Provide some mechanism to input
arbitrary radio frequencies. Could be like AltosUI which allows
you to edit the list of frequencies and assign names to them,
*) Remember most-recently-used TBT and frequency, perhaps
auto-connect at startup.
+
+ Done
*) Re-loading flight data from .telem file to get back to
'find my rocket' mode after shutting down the application.
+ Done
+
*) Imperial Units mode
+ Done
+
*) TeleBT battery voltage
android:layout_width="match_parent"
android:layout_height="match_parent"
>
- <TextView android:id="@+id/title_paired_devices"
+ <Button android:id="@+id/button_scan"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/title_paired_devices"
+ android:text="@string/button_scan"
+ />
+ <TextView android:id="@+id/title_new_devices"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/title_other_devices"
android:visibility="gone"
android:background="#666"
android:textColor="#fff"
android:paddingLeft="5dp"
/>
- <ListView android:id="@+id/paired_devices"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:stackFromBottom="true"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/title_new_devices"
+ <TextView android:id="@+id/title_paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/title_other_devices"
+ android:text="@string/title_paired_devices"
android:visibility="gone"
android:background="#666"
android:textColor="#fff"
android:paddingLeft="5dp"
/>
- <ListView android:id="@+id/new_devices"
+ <ListView android:id="@+id/paired_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stackFromBottom="true"
- android:layout_weight="2"
+ android:layout_weight="1"
/>
- <Button android:id="@+id/button_scan"
+ <ListView android:id="@+id/new_devices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/button_scan"
+ android:stackFromBottom="true"
+ android:layout_weight="2"
/>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
<item android:id="@+id/connect_scan"
android:icon="@android:drawable/ic_menu_search"
android:title="@string/connect_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/select_rate"
android:icon="@android:drawable/ic_menu_preferences"
android:title="@string/select_rate" />
+ <item android:id="@+id/change_units"
+ android:icon="@android:drawable/ic_menu_view"
+ android:title="@string/change_units" />
</menu>
<!-- AltosDroid -->
<string name="bt_not_enabled">Bluetooth was not enabled.</string>
<string name="title_connecting">connecting…</string>
- <string name="title_connected_to">connected: </string>
+ <string name="title_connected_to">connected</string>
<string name="title_not_connected">not connected</string>
<!-- Options Menu -->
<string name="connect_device">Connect a device</string>
+ <string name="quit">Quit</string>
<string name="select_freq">Select radio frequency</string>
<string name="select_rate">Select data rate</string>
+ <string name="change_units">Change units</string>
<!-- DeviceListActivity -->
<string name="scanning">scanning for devices…</string>
// Constructor
public AltosBluetooth(BluetoothDevice in_device, Handler in_handler) {
+// set_debug(D);
adapter = BluetoothAdapter.getDefaultAdapter();
device = in_device;
handler = in_handler;
}
}
+ public double frequency() {
+ return frequency;
+ }
+
+ public int telemetry_rate() {
+ return telemetry_rate;
+ }
+
+ public void save_frequency() {
+ AltosPreferences.set_frequency(0, frequency);
+ }
+
+ public void save_telemetry_rate() {
+ AltosPreferences.set_telemetry_rate(0, telemetry_rate);
+ }
+
private synchronized void wait_connected() throws InterruptedException, IOException {
if (input == null) {
+ if (D) Log.d(TAG, "wait_connected...");
wait();
+ if (D) Log.d(TAG, "wait_connected done");
if (input == null) throw new IOException();
}
}
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.Window;
+import android.view.View;
import android.widget.TabHost;
import android.widget.TextView;
+import android.widget.RelativeLayout;
import android.widget.Toast;
import android.app.AlertDialog;
import android.location.Location;
import org.altusmetrum.altoslib_5.*;
-public class AltosDroid extends FragmentActivity {
+public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
// Debugging
static final String TAG = "AltosDroid";
static final boolean D = true;
// Message types received by our Handler
- public static final int MSG_STATE_CHANGE = 1;
- public static final int MSG_TELEMETRY = 2;
- public static final int MSG_UPDATE_AGE = 3;
- public static final int MSG_LOCATION = 4;
- public static final int MSG_CRC_ERROR = 5;
+
+ public static final int MSG_STATE = 1;
+ public static final int MSG_UPDATE_AGE = 2;
// Intent request codes
- private static final int REQUEST_CONNECT_DEVICE = 1;
- private static final int REQUEST_ENABLE_BT = 2;
+ public static final int REQUEST_CONNECT_DEVICE = 1;
+ public static final int REQUEST_ENABLE_BT = 2;
public static FragmentManager fm;
+ private BluetoothAdapter mBluetoothAdapter = null;
+
// Layout Views
private TextView mTitle;
private TextView mRSSIView;
private TextView mSerialView;
private TextView mFlightView;
+ private RelativeLayout mStateLayout;
private TextView mStateView;
private TextView mAgeView;
// field to display the version at the bottom of the screen
private TextView mVersion;
+ private double frequency;
+ private int telemetry_rate;
+
// Tabs
TabHost mTabHost;
AltosViewPager mViewPager;
int tabHeight;
// Timer and Saved flight state for Age calculation
- private Timer timer = new Timer();
+ private Timer timer;
AltosState saved_state;
- Location saved_location;
// Service
private boolean mIsBound = false;
private Messenger mService = null;
final Messenger mMessenger = new Messenger(new IncomingHandler(this));
- // Preferences
- private AltosDroidPreferences prefs = null;
-
- // TeleBT Config data
- private AltosConfigData mConfigData = null;
- // Local Bluetooth adapter
- private BluetoothAdapter mBluetoothAdapter = null;
-
// Text to Speech
private AltosVoice mAltosVoice = null;
@Override
public void handleMessage(Message msg) {
AltosDroid ad = mAltosDroid.get();
+
switch (msg.what) {
- case MSG_STATE_CHANGE:
- if(D) Log.d(TAG, "MSG_STATE_CHANGE: " + msg.arg1);
- switch (msg.arg1) {
- case TelemetryService.STATE_CONNECTED:
- ad.mConfigData = (AltosConfigData) msg.obj;
- String str = String.format(" %s S/N: %d", ad.mConfigData.product, ad.mConfigData.serial);
- ad.mTitle.setText(R.string.title_connected_to);
- ad.mTitle.append(str);
- Toast.makeText(ad.getApplicationContext(), "Connected to " + str, Toast.LENGTH_SHORT).show();
- break;
- case TelemetryService.STATE_CONNECTING:
- ad.mTitle.setText(R.string.title_connecting);
- break;
- case TelemetryService.STATE_READY:
- case TelemetryService.STATE_NONE:
- ad.mConfigData = null;
- ad.mTitle.setText(R.string.title_not_connected);
- break;
+ case MSG_STATE:
+ if(D) Log.d(TAG, "MSG_STATE");
+ TelemetryState telemetry_state = (TelemetryState) msg.obj;
+ if (telemetry_state == null) {
+ Log.d(TAG, "telemetry_state null!");
+ return;
}
- break;
- case MSG_TELEMETRY:
- ad.update_ui((AltosState) msg.obj);
- break;
- case MSG_LOCATION:
- ad.set_location((Location) msg.obj);
- break;
- case MSG_CRC_ERROR:
+
+ ad.update_state(telemetry_state);
break;
case MSG_UPDATE_AGE:
- if (ad.saved_state != null) {
- ad.mAgeView.setText(String.format("%d", (System.currentTimeMillis() - ad.saved_state.received_time + 500) / 1000));
- }
+ if(D) Log.d(TAG, "MSG_UPDATE_AGE");
+ ad.update_age();
break;
}
}
mTabs.remove(mTab);
}
- void set_location(Location location) {
- saved_location = location;
- Log.d(TAG, "set_location");
- update_ui(saved_state);
+ public void units_changed(boolean imperial_units) {
+ for (AltosDroidTab mTab : mTabs)
+ mTab.units_changed(imperial_units);
+ }
+
+ void update_title(TelemetryState telemetry_state) {
+ switch (telemetry_state.connect) {
+ case TelemetryState.CONNECT_CONNECTED:
+ if (telemetry_state.config != null) {
+ String str = String.format("S/N %d %6.3f MHz", telemetry_state.config.serial,
+ telemetry_state.frequency);
+ 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);
+ } else {
+ mTitle.setText(R.string.title_connected_to);
+ }
+ break;
+ case TelemetryState.CONNECT_CONNECTING:
+ mTitle.setText(R.string.title_connecting);
+ break;
+ case TelemetryState.CONNECT_READY:
+ case TelemetryState.CONNECT_NONE:
+ mTitle.setText(R.string.title_not_connected);
+ break;
+ }
+ }
+
+ void start_timer() {
+ if (timer == null) {
+ timer = new Timer();
+ timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 1000L, 1000L);
+ }
+ }
+
+ void stop_timer() {
+ if (timer != null) {
+ timer.cancel();
+ timer.purge();
+ timer = null;
+ }
+ }
+
+ boolean registered_units_listener;
+
+ void update_state(TelemetryState telemetry_state) {
+
+ if (!registered_units_listener) {
+ registered_units_listener = true;
+ AltosPreferences.register_units_listener(this);
+ }
+
+ update_title(telemetry_state);
+ update_ui(telemetry_state.state, telemetry_state.location);
+ if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED)
+ start_timer();
+ else
+ stop_timer();
}
boolean same_string(String a, String b) {
}
}
- void update_ui(AltosState state) {
+ void update_age() {
+ if (saved_state != null)
+ mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000));
+ }
+
+ void update_ui(AltosState state, Location location) {
Log.d(TAG, "update_ui");
- if (state != null && saved_state != null) {
- if (saved_state.state != state.state) {
- String currentTab = mTabHost.getCurrentTabTag();
- Log.d(TAG, "switch state");
- switch (state.state) {
- case AltosLib.ao_flight_boost:
- if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent");
- break;
- case AltosLib.ao_flight_drogue:
- if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent");
- break;
- case AltosLib.ao_flight_landed:
- if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed");
- break;
- }
- }
- }
+
+ int prev_state = AltosLib.ao_flight_invalid;
AltosGreatCircle from_receiver = null;
- if (state != null && saved_location != null && state.gps != null && state.gps.locked) {
- double altitude = 0;
- if (saved_location.hasAltitude())
- altitude = saved_location.getAltitude();
- from_receiver = new AltosGreatCircle(saved_location.getLatitude(),
- saved_location.getLongitude(),
- altitude,
- state.gps.lat,
- state.gps.lon,
- state.gps.alt);
- }
+ if (saved_state != null)
+ prev_state = saved_state.state;
if (state != null) {
+ Log.d(TAG, String.format("prev state %d new state %d\n", prev_state, state.state));
+ if (state.state == AltosLib.ao_flight_stateless) {
+ boolean prev_locked = false;
+ boolean locked = false;
+
+ if(state.gps != null)
+ locked = state.gps.locked;
+ if (saved_state != null && saved_state.gps != null)
+ prev_locked = saved_state.gps.locked;
+ if (prev_locked != locked) {
+ String currentTab = mTabHost.getCurrentTabTag();
+ if (locked) {
+ if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+ } else {
+ if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("pad");
+ }
+ }
+ } else {
+ if (prev_state != state.state) {
+ String currentTab = mTabHost.getCurrentTabTag();
+ Log.d(TAG, "switch state");
+ switch (state.state) {
+ case AltosLib.ao_flight_boost:
+ if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent");
+ break;
+ case AltosLib.ao_flight_drogue:
+ if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent");
+ break;
+ case AltosLib.ao_flight_landed:
+ if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed");
+ break;
+ case AltosLib.ao_flight_stateless:
+ if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+ break;
+ }
+ }
+ }
+
+ if (location != null && state.gps != null && state.gps.locked) {
+ double altitude = 0;
+ if (location.hasAltitude())
+ altitude = location.getAltitude();
+ from_receiver = new AltosGreatCircle(location.getLatitude(),
+ location.getLongitude(),
+ altitude,
+ state.gps.lat,
+ state.gps.lon,
+ state.gps.alt);
+ }
+
if (saved_state == null || !same_string(saved_state.callsign, state.callsign)) {
Log.d(TAG, "update callsign");
mCallsignView.setText(state.callsign);
}
if (saved_state == null || state.state != saved_state.state) {
Log.d(TAG, "update state");
- mStateView.setText(state.state_name());
+ if (state.state == AltosLib.ao_flight_stateless) {
+ mStateLayout.setVisibility(View.GONE);
+ } else {
+ mStateView.setText(state.state_name());
+ mStateLayout.setVisibility(View.VISIBLE);
+ }
}
if (saved_state == null || state.rssi != saved_state.rssi) {
Log.d(TAG, "update rssi");
}
for (AltosDroidTab mTab : mTabs)
- mTab.update_ui(state, from_receiver, saved_location, mTab == mTabsAdapter.currentItem());
+ mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem());
if (state != null)
- mAltosVoice.tell(state);
+ mAltosVoice.tell(state, from_receiver);
saved_state = state;
}
super.onCreate(savedInstanceState);
if(D) Log.e(TAG, "+++ ON CREATE +++");
- fm = getSupportFragmentManager();
-
// Get local Bluetooth adapter
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
finish();
- return;
}
- // Initialise preferences
- prefs = new AltosDroidPreferences(this);
- AltosPreferences.init(prefs);
+ fm = getSupportFragmentManager();
// Set up the window layout
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
for (int i = 0; i < 5; i++)
mTabHost.getTabWidget().getChildAt(i).getLayoutParams().height = tabHeight;
-
// Set up the custom title
mTitle = (TextView) findViewById(R.id.title_left_text);
mTitle.setText(R.string.app_name);
mRSSIView = (TextView) findViewById(R.id.rssi_value);
mSerialView = (TextView) findViewById(R.id.serial_value);
mFlightView = (TextView) findViewById(R.id.flight_value);
+ mStateLayout = (RelativeLayout) findViewById(R.id.state_container);
mStateView = (TextView) findViewById(R.id.state_value);
mAgeView = (TextView) findViewById(R.id.age_value);
- timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 1000L, 100L);
-
mAltosVoice = new AltosVoice(this);
}
super.onStart();
if(D) Log.e(TAG, "++ ON START ++");
+ // Start Telemetry Service
+ startService(new Intent(AltosDroid.this, TelemetryService.class));
+
if (!mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
- startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
+ startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
}
- // Start Telemetry Service
- startService(new Intent(AltosDroid.this, TelemetryService.class));
-
doBindService();
+
}
@Override
if(D) Log.e(TAG, "--- ON DESTROY ---");
if (mAltosVoice != null) mAltosVoice.stop();
+ stop_timer();
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
}
- private void connectDevice(Intent data) {
- // Get the device MAC address
- String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
- // Get the BLuetoothDevice object
- BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+ private void connectDevice(String address) {
// Attempt to connect to the device
try {
- if (D) Log.d(TAG, "Connecting to " + device.getName());
- mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, device));
+ if (D) Log.d(TAG, "Connecting to " + address);
+ mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, address));
} catch (RemoteException e) {
}
}
+ private void connectDevice(Intent data) {
+ // Get the device MAC address
+ String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
+ connectDevice(address);
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
return true;
+ case R.id.quit:
+ Log.d(TAG, "R.id.quit");
+ stopService(new Intent(AltosDroid.this, TelemetryService.class));
+ finish();
+ return true;
case R.id.select_freq:
// Set the TBT radio frequency
AlertDialog alert_rate = builder_rate.create();
alert_rate.show();
return true;
+ case R.id.change_units:
+ boolean imperial = AltosPreferences.imperial_units();
+ AltosPreferences.set_imperial_units(!imperial);
+ return true;
}
return false;
}
/*
- * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ * 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
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-
package org.altusmetrum.AltosDroid;
-import java.io.File;
-import java.util.Map;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Environment;
-
import org.altusmetrum.altoslib_5.*;
-public class AltosDroidPreferences implements AltosPreferencesBackend {
- public final static String NAME = "org.altusmetrum.AltosDroid";
- private Context context = null;
- private SharedPreferences prefs = null;
- private SharedPreferences.Editor editor = null;
-
- public AltosDroidPreferences(Context in_context) {
- this(in_context, NAME);
- }
-
- public AltosDroidPreferences(Context in_context, String in_prefs) {
- context = in_context;
- prefs = context.getSharedPreferences(in_prefs, 0);
- editor = prefs.edit();
- }
-
- public String[] keys() {
- Map<String, ?> all = prefs.getAll();
- return (String[])all.keySet().toArray();
- }
-
- public AltosPreferencesBackend node(String key) {
- return new AltosDroidPreferences(context, key);
- }
-
- public boolean nodeExists(String key) {
- return prefs.contains(key);
- }
-
- public boolean getBoolean(String key, boolean def) {
- return prefs.getBoolean(key, def);
- }
-
- public double getDouble(String key, double def) {
- Float f = Float.valueOf(prefs.getFloat(key, (float)def));
- return f.doubleValue();
- }
-
- public int getInt(String key, int def) {
- return prefs.getInt(key, def);
- }
-
- public String getString(String key, String def) {
- return prefs.getString(key, def);
- }
+public class AltosDroidPreferences extends AltosPreferences {
- public void putBoolean(String key, boolean value) {
- editor.putBoolean(key, value);
- }
+ /* Active device preference name */
+ final static String activeDevicePreference = "ACTIVE-DEVICE";
- public void putDouble(String key, double value) {
- editor.putFloat(key, (float)value);
- }
+ static String active_device_address;
- public void putInt(String key, int value) {
- editor.putInt(key, value);
- }
+ public static void init(Context context) {
+ if (backend != null)
+ return;
- public void putString(String key, String value) {
- editor.putString(key, value);
- }
+ AltosPreferences.init(new AltosDroidPreferencesBackend(context));
- public void remove(String key) {
- editor.remove(key);
+ active_device_address = backend.getString(activeDevicePreference, null);
}
- public void flush() {
- editor.apply();
+ public static void set_active_device(String address) {
+ synchronized(backend) {
+ active_device_address = address;
+ backend.putString(activeDevicePreference, active_device_address);
+ flush_preferences();
+ }
}
- public File homeDirectory() {
- return Environment.getExternalStorageDirectory();
+ public static String active_device() {
+ synchronized(backend) {
+ return active_device_address;
+ }
}
}
--- /dev/null
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * 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.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.io.File;
+import java.util.Map;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+
+import org.altusmetrum.altoslib_5.*;
+
+public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {
+ public final static String NAME = "org.altusmetrum.AltosDroid";
+ private Context context = null;
+ private SharedPreferences prefs = null;
+ private SharedPreferences.Editor editor = null;
+
+ public AltosDroidPreferencesBackend(Context in_context) {
+ this(in_context, NAME);
+ }
+
+ public AltosDroidPreferencesBackend(Context in_context, String in_prefs) {
+ context = in_context;
+ prefs = context.getSharedPreferences(in_prefs, 0);
+ editor = prefs.edit();
+ }
+
+ public String[] keys() {
+ Map<String, ?> all = prefs.getAll();
+ return (String[])all.keySet().toArray();
+ }
+
+ public AltosPreferencesBackend node(String key) {
+ return new AltosDroidPreferencesBackend(context, key);
+ }
+
+ public boolean nodeExists(String key) {
+ return prefs.contains(key);
+ }
+
+ public boolean getBoolean(String key, boolean def) {
+ return prefs.getBoolean(key, def);
+ }
+
+ public double getDouble(String key, double def) {
+ Float f = Float.valueOf(prefs.getFloat(key, (float)def));
+ return f.doubleValue();
+ }
+
+ public int getInt(String key, int def) {
+ return prefs.getInt(key, def);
+ }
+
+ public String getString(String key, String def) {
+ return prefs.getString(key, def);
+ }
+
+ public void putBoolean(String key, boolean value) {
+ editor.putBoolean(key, value);
+ }
+
+ public void putDouble(String key, double value) {
+ editor.putFloat(key, (float)value);
+ }
+
+ public void putInt(String key, int value) {
+ editor.putInt(key, value);
+ }
+
+ public void putString(String key, String value) {
+ editor.putString(key, value);
+ }
+
+ public void remove(String key) {
+ editor.remove(key);
+ }
+
+ public void flush() {
+ editor.apply();
+ }
+
+ public File homeDirectory() {
+ return Environment.getExternalStorageDirectory();
+ }
+}
import android.support.v4.app.FragmentManager;
import android.location.Location;
import android.util.Log;
+import android.widget.TextView;
-public abstract class AltosDroidTab extends Fragment {
+public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
AltosState last_state;
AltosGreatCircle last_from_receiver;
Location last_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);
+ }
+
+ public void set_value(TextView text_view,
+ AltosUnits units,
+ int width,
+ double value) {
+ if (value == AltosLib.MISSING)
+ text_view.setText("");
+ else
+ text_view.setText(units.show(width, value));
+ }
+
public void set_visible(boolean visible) {
FragmentTransaction ft = AltosDroid.fm.beginTransaction();
- if (visible)
+ if (visible) {
+ AltosState state = last_state;
+ AltosGreatCircle from_receiver = last_from_receiver;
+ Location receiver = last_receiver;
+
+ show(state, from_receiver, receiver);
ft.show(this);
- else
+ } else
ft.hide(this);
ft.commit();
}
public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) {
+ last_state = state;
+ last_from_receiver = from_receiver;
+ last_receiver = receiver;
if (is_current) {
- Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name()));
+ if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name()));
show(state, from_receiver, receiver);
} else {
- Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name()));
- last_state = state;
- last_from_receiver = from_receiver;
- last_receiver = receiver;
+ if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name()));
return;
}
}
-
- public void onHiddenChanged(boolean hidden) {
- if (last_state != null && isVisible()) {
- AltosState state = last_state;
- AltosGreatCircle from_receiver = last_from_receiver;
- Location receiver = last_receiver;
-
- last_state = null;
- last_from_receiver = null;
- last_receiver = null;
- show(state, from_receiver, receiver);
- }
- }
}
}
}
- public void tell(AltosState state) {
+ public void tell(AltosState state, AltosGreatCircle from_receiver) {
if (!tts_enabled) return;
boolean spoke = false;
if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&
state.state > AltosLib.ao_flight_boost) {
if (state.max_speed() != AltosLib.MISSING)
- speak(String.format("max speed: %d meters per second.", (int) (state.max_speed() + 0.5)));
+ speak(String.format("Max speed: %s.",
+ AltosConvert.speed.say_units(state.max_speed())));
spoke = true;
} else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&
state.state >= AltosLib.ao_flight_drogue) {
if (state.max_height() != AltosLib.MISSING)
- speak(String.format("max height: %d meters.", (int) (state.max_height() + 0.5)));
+ speak(String.format("Max height: %s.",
+ AltosConvert.height.say_units(state.max_height())));
spoke = true;
}
}
}
old_state = state;
if (idle_thread != null)
- idle_thread.notice(state, spoke);
+ idle_thread.notice(state, from_receiver, spoke);
}
class IdleThread extends Thread {
boolean started;
private AltosState state;
+ private AltosGreatCircle from_receiver;
int reported_landing;
int report_interval;
long report_time;
return;
}
- /* If the rocket isn't on the pad, then report height */
- if (((AltosLib.ao_flight_drogue <= state.state &&
+ /* If the rocket isn't on the pad, then report location */
+ if ((AltosLib.ao_flight_drogue <= state.state &&
state.state < AltosLib.ao_flight_landed) ||
- state.state == AltosLib.ao_flight_stateless) &&
- state.range >= 0)
+ state.state == AltosLib.ao_flight_stateless)
{
- if (state.from_pad != null) {
- speak(String.format("Height %d, bearing %s %d, elevation %d, range %d.\n",
- (int) (state.height() + 0.5),
- state.from_pad.bearing_words(
+ AltosGreatCircle position;
+
+ if (from_receiver != null)
+ position = from_receiver;
+ else
+ position = state.from_pad;
+
+ if (position != null) {
+ speak(String.format("Height %s, bearing %s %d, elevation %d, range %s.\n",
+ AltosConvert.height.say_units(state.height()),
+ position.bearing_words(
AltosGreatCircle.BEARING_VOICE),
- (int) (state.from_pad.bearing + 0.5),
- (int) (state.elevation + 0.5),
- (int) (state.range + 0.5)));
- } else {
- speak(String.format("Height %d, elevation %d, range %d.\n",
- (int) (state.height() + 0.5),
- (int) (state.elevation + 0.5),
- (int) (state.range + 0.5)));
+ (int) (position.bearing + 0.5),
+ (int) (position.elevation + 0.5),
+ AltosConvert.distance.say_units(position.range)));
}
} else if (state.state > AltosLib.ao_flight_pad) {
if (state.height() != AltosLib.MISSING)
- speak(String.format("%d meters", (int) (state.height() + 0.5)));
+ speak(AltosConvert.height.say_units(state.height()));
} else {
reported_landing = 0;
}
else
speak("rocket may have crashed");
if (state.from_pad != null)
- speak(String.format("Bearing %d degrees, range %d meters.",
+ speak(String.format("Bearing %d degrees, range %s.",
(int) (state.from_pad.bearing + 0.5),
- (int) (state.from_pad.distance + 0.5)));
+ AltosConvert.distance.say_units(state.from_pad.distance)));
++reported_landing;
}
}
}
}
- public synchronized void notice(AltosState new_state, boolean spoken) {
+ public synchronized void notice(AltosState new_state, AltosGreatCircle new_from_receiver, boolean spoken) {
AltosState old_state = state;
state = new_state;
+ from_receiver = new_from_receiver;
if (!started && state.state > AltosLib.ao_flight_pad) {
started = true;
start();
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
+import android.view.View;
public class GoNoGoLights {
private Boolean state;
missing = m;
set = true;
if (missing) {
+ hide();
red.setImageDrawable(dGray);
green.setImageDrawable(dGray);
} else if (state) {
red.setImageDrawable(dGray);
green.setImageDrawable(dGreen);
+ show();
} else {
red.setImageDrawable(dRed);
green.setImageDrawable(dGray);
+ show();
}
}
+
+ public void show() {
+ red.setVisibility(View.VISIBLE);
+ green.setVisibility(View.VISIBLE);
+ }
+
+ public void hide() {
+ red.setVisibility(View.GONE);
+ green.setVisibility(View.GONE);
+ }
}
public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (state != null) {
- mHeightView.setText(AltosDroid.number("%6.0f m", state.height()));
- mMaxHeightView.setText(AltosDroid.number("%6.0f m", state.max_height()));
- mSpeedView.setText(AltosDroid.number("%6.0f m/s", state.speed()));
- mMaxSpeedView.setText(AltosDroid.number("%6.0f m/s", state.max_speed()));
- mAccelView.setText(AltosDroid.number("%6.0f m/s²", state.acceleration()));
- mMaxAccelView.setText(AltosDroid.number("%6.0f m/s²", state.max_acceleration()));
+ set_value(mHeightView, AltosConvert.height, 6, state.height());
+ set_value(mHeightView, AltosConvert.height, 6, state.height());
+ set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
+ set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
+ set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
+ set_value(mAccelView, AltosConvert.accel, 6, state.acceleration());
+ set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
if (state.gps != null) {
mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (state != null) {
- mSpeedView.setText(AltosDroid.number("%6.0f m/s", state.speed()));
- mHeightView.setText(AltosDroid.number("%6.0f m", state.height()));
+ set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
+ set_value(mHeightView, AltosConvert.height, 6, state.height());
if (from_receiver != null) {
mElevationView.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
- mRangeView.setText(AltosDroid.number("%6.0f m", from_receiver.range));
+ set_value(mRangeView, AltosConvert.distance, 6, from_receiver.range);
mBearingView.setText(AltosDroid.number("%3.0f°", from_receiver.bearing));
mCompassView.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
- mDistanceView.setText(AltosDroid.number("%6.0f m", from_receiver.distance));
+ set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
} else {
mElevationView.setText("<unknown>");
mRangeView.setText("<unknown>");
public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (from_receiver != null) {
mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
- mDistanceView.setText(String.format("%6.0f m", from_receiver.distance));
+ set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
}
if (state != null && state.gps != null) {
mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
}
if (state != null) {
- mMaxHeightView.setText(String.format("%6.0f m", state.max_height()));
- mMaxAccelView.setText(String.format("%6.0f m/s²", state.max_acceleration()));
- mMaxSpeedView.setText(String.format("%6.0f m/s", state.max_speed()));
+ set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
+ set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
+ set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
}
}
}
public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
if (from_receiver != null) {
mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
- mDistanceView.setText(String.format("%6.0f m", from_receiver.distance));
+ set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
}
if (state != null) {
AltosDroid mAltosDroid;
private TextView mBatteryVoltageView;
+ private TextView mBatteryVoltageLabel;
private GoNoGoLights mBatteryLights;
private TextView mApogeeVoltageView;
+ private TextView mApogeeVoltageLabel;
private GoNoGoLights mApogeeLights;
private TextView mMainVoltageView;
+ private TextView mMainVoltageLabel;
private GoNoGoLights mMainLights;
private TextView mDataLoggingView;
private GoNoGoLights mDataLoggingLights;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tab_pad, container, false);
mBatteryVoltageView = (TextView) v.findViewById(R.id.battery_voltage_value);
+ mBatteryVoltageLabel = (TextView) v.findViewById(R.id.battery_voltage_label);
mBatteryLights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
(ImageView) v.findViewById(R.id.battery_greenled),
getResources());
mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value);
+ mApogeeVoltageLabel = (TextView) v.findViewById(R.id.apogee_voltage_label);
mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
(ImageView) v.findViewById(R.id.apogee_greenled),
getResources());
mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value);
+ mMainVoltageLabel = (TextView) v.findViewById(R.id.main_voltage_label);
mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
(ImageView) v.findViewById(R.id.main_greenled),
getResources());
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);
-
- mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
+ if (state.apogee_voltage == AltosLib.MISSING) {
+ mApogeeVoltageView.setVisibility(View.GONE);
+ mApogeeVoltageLabel.setVisibility(View.GONE);
+ } else {
+ mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
+ mApogeeVoltageView.setVisibility(View.VISIBLE);
+ mApogeeVoltageLabel.setVisibility(View.VISIBLE);
+ }
mApogeeLights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
-
- mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
+ if (state.main_voltage == AltosLib.MISSING) {
+ mMainVoltageView.setVisibility(View.GONE);
+ mMainVoltageLabel.setVisibility(View.GONE);
+ } else {
+ mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
+ mMainVoltageView.setVisibility(View.VISIBLE);
+ mMainVoltageLabel.setVisibility(View.VISIBLE);
+ }
mMainLights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
if (state.flight != 0) {
}
if (receiver != null) {
- double altitude = 0;
+ double altitude = AltosLib.MISSING;
if (receiver.hasAltitude())
altitude = receiver.getAltitude();
mPadLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
mPadLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "W", "E"));
- mPadAltitudeView.setText(AltosDroid.number("%4.0f m", altitude));
+ set_value(mPadAltitudeView, AltosConvert.height, 6, altitude);
}
}
public class TelemetryReader extends Thread {
private static final String TAG = "TelemetryReader";
+ private static final boolean D = true;
int crc_errors;
AltosLink link;
AltosState state = null;
+ AltosFlightReader stacked;
+
LinkedBlockingQueue<AltosLine> telemQueue;
public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException {
public void close() {
state = null;
+ if (stacked != null) {
+ stacked.close(false);
+ stacked = null;
+ }
link.remove_monitor(telemQueue);
link = null;
telemQueue.clear();
AltosState state = null;
try {
- for (;;) {
+ if (D) Log.d(TAG, "starting reader");
+ while (stacked != null) {
+ AltosState stacked_state = null;
+ try {
+ stacked_state = stacked.read();
+ } catch (ParseException pe) {
+ continue;
+ } catch (AltosCRCException ce) {
+ continue;
+ }
+ if (stacked_state != null)
+ state = stacked_state;
+ else
+ stacked = null;
+ }
+ if (state != null) {
+ if (D) Log.d(TAG, "Send initial state");
+ handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();
+ }
+ if (D) Log.d(TAG, "starting loop");
+ while (telemQueue != null) {
try {
state = read();
handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();
}
}
- public TelemetryReader (AltosLink in_link, Handler in_handler) {
+ public TelemetryReader (AltosLink in_link, Handler in_handler, AltosFlightReader in_stacked) {
+ if (D) Log.d(TAG, "connected TelemetryReader create started");
link = in_link;
handler = in_handler;
+ stacked = in_stacked;
state = null;
telemQueue = new LinkedBlockingQueue<AltosLine>();
link.add_monitor(telemQueue);
+ link.set_telemetry(AltosLib.ao_telemetry_standard);
+
+ if (D) Log.d(TAG, "connected TelemetryReader created");
+ }
+
+ private static AltosFlightReader existing_data(AltosLink link) {
+ if (link == null)
+ return null;
+
+ File file = AltosPreferences.logfile(link.serial);
+ if (file != null) {
+ AltosStateIterable iterable = AltosStateIterable.iterable(file);
+ if (iterable != null)
+ return new AltosReplayReader(iterable.iterator(), file, false);
+ }
+ return null;
+ }
+
+ public TelemetryReader(AltosLink link, Handler handler) {
+ this(link, handler, existing_data(link));
}
}
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.content.Context;
import android.os.Bundle;
static final int MSG_CRC_ERROR = 9;
static final int MSG_SETBAUD = 10;
- public static final int STATE_NONE = 0;
- public static final int STATE_READY = 1;
- public static final int STATE_CONNECTING = 2;
- public static final int STATE_CONNECTED = 3;
-
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION = R.string.telemetry_service_label;
final Messenger mMessenger = new Messenger(mHandler); // Target we publish for clients to send messages to IncomingHandler.
// Name of the connected device
- private BluetoothDevice device = null;
+ String address;
private AltosBluetooth mAltosBluetooth = null;
- private AltosConfigData mConfigData = null;
private TelemetryReader mTelemetryReader = null;
private TelemetryLogger mTelemetryLogger = null;
+ // Local Bluetooth adapter
+ private BluetoothAdapter mBluetoothAdapter = null;
- // internally track state of bluetooth connection
- private int state = STATE_NONE;
+ private TelemetryState telemetry_state;
// Last data seen; send to UI when it starts
- private AltosState last_state;
- private Location last_location;
- private int last_crc_errors;
-
// Handler of incoming messages from clients.
static class IncomingHandler extends Handler {
private final WeakReference<TelemetryService> service;
@Override
public void handleMessage(Message msg) {
TelemetryService s = service.get();
+ if (s == null)
+ return;
switch (msg.what) {
case MSG_REGISTER_CLIENT:
s.mClients.add(msg.replyTo);
try {
// Now we try to send the freshly connected UI any relavant information about what
- // we're talking to - Basically state and Config Data.
- msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_STATE_CHANGE, s.state, -1, s.mConfigData));
- // We also send any recent telemetry or location data that's cached
- if (s.last_state != null) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_TELEMETRY, s.last_state ));
- if (s.last_location != null) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_LOCATION , s.last_location ));
- if (s.last_crc_errors != 0 ) msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_CRC_ERROR, s.last_crc_errors));
+ // we're talking to
+ msg.replyTo.send(s.message());
} catch (RemoteException e) {
s.mClients.remove(msg.replyTo);
}
break;
case MSG_CONNECT:
if (D) Log.d(TAG, "Connect command received");
- s.device = (BluetoothDevice) msg.obj;
- s.startAltosBluetooth();
+ String address = (String) msg.obj;
+ AltosDroidPreferences.set_active_device(address);
+ s.startAltosBluetooth(address);
break;
case MSG_CONNECTED:
if (D) Log.d(TAG, "Connected to device");
- s.connected();
+ try {
+ s.connected();
+ } catch (InterruptedException ie) {
+ }
break;
case MSG_CONNECT_FAILED:
if (D) Log.d(TAG, "Connection failed... retrying");
- s.startAltosBluetooth();
+ if (s.address != null)
+ s.startAltosBluetooth(s.address);
break;
case MSG_DISCONNECTED:
- // Only do the following if we haven't been shutdown elsewhere..
- if (s.device != null) {
- if (D) Log.d(TAG, "Disconnected from " + s.device.getName());
- s.stopAltosBluetooth();
- }
+ Log.d(TAG, "MSG_DISCONNECTED");
+ s.stopAltosBluetooth();
break;
case MSG_TELEMETRY:
// forward telemetry messages
- s.last_state = (AltosState) msg.obj;
- s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_TELEMETRY, msg.obj));
+ s.telemetry_state.state = (AltosState) msg.obj;
+ if (D) Log.d(TAG, "MSG_TELEMETRY");
+ s.sendMessageToClients();
break;
case MSG_CRC_ERROR:
// forward crc error messages
- s.last_crc_errors = (Integer) msg.obj;
- s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_CRC_ERROR, msg.obj));
+ s.telemetry_state.crc_errors = (Integer) msg.obj;
+ if (D) Log.d(TAG, "MSG_CRC_ERROR");
+ s.sendMessageToClients();
break;
case MSG_SETFREQUENCY:
- if (s.state == STATE_CONNECTED) {
+ if (D) Log.d(TAG, "MSG_SETFREQUENCY");
+ s.telemetry_state.frequency = (Double) msg.obj;
+ if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
try {
- s.mAltosBluetooth.set_radio_frequency((Double) msg.obj);
+ s.mAltosBluetooth.set_radio_frequency(s.telemetry_state.frequency);
+ s.mAltosBluetooth.save_frequency();
} catch (InterruptedException e) {
} catch (TimeoutException e) {
}
}
+ s.sendMessageToClients();
break;
case MSG_SETBAUD:
- if (s.state == STATE_CONNECTED) {
- s.mAltosBluetooth.set_telemetry_rate((Integer) msg.obj);
+ if (D) Log.d(TAG, "MSG_SETBAUD");
+ s.telemetry_state.telemetry_rate = (Integer) msg.obj;
+ if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
+ s.mAltosBluetooth.set_telemetry_rate(s.telemetry_state.telemetry_rate);
+ s.mAltosBluetooth.save_telemetry_rate();
}
+ s.sendMessageToClients();
break;
default:
super.handleMessage(msg);
}
}
- private void sendMessageToClients(Message m) {
+ private Message message() {
+ if (telemetry_state == null)
+ Log.d(TAG, "telemetry_state null!");
+ return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
+ }
+
+ private void sendMessageToClients() {
+ Message m = message();
+ if (D) Log.d(TAG, String.format("Send message to %d clients", mClients.size()));
for (int i=mClients.size()-1; i>=0; i--) {
try {
+ if (D) Log.d(TAG, String.format("Send message to client %d", i));
mClients.get(i).send(m);
} catch (RemoteException e) {
mClients.remove(i);
private void stopAltosBluetooth() {
if (D) Log.d(TAG, "stopAltosBluetooth(): begin");
- setState(STATE_READY);
+ telemetry_state.connect = TelemetryState.CONNECT_READY;
if (mTelemetryReader != null) {
if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryReader");
mTelemetryReader.interrupt();
mAltosBluetooth.close();
mAltosBluetooth = null;
}
- device = null;
- mConfigData = null;
+ telemetry_state.config = null;
+ if (D) Log.d(TAG, "stopAltosBluetooth(): send message to clients");
+ sendMessageToClients();
}
- private void startAltosBluetooth() {
- if (device == null) {
- return;
- }
+ private void startAltosBluetooth(String address) {
+ // Get the BLuetoothDevice object
+ BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+
+ this.address = address;
if (mAltosBluetooth == null) {
if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()));
mAltosBluetooth = new AltosBluetooth(device, mHandler);
- setState(STATE_CONNECTING);
+ telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
+ sendMessageToClients();
} else {
// This is a bit of a hack - if it appears we're still connected, we treat this as a restart.
// So, to give a suitable delay to teardown/bringup, we just schedule a resend of a message
// ... then we tear down the existing connection.
// We do it this way around so that we don't lose a reference to the device when this method
// is called on reception of MSG_CONNECT_FAILED in the handler above.
- mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, device), 3000);
+ mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, address), 3000);
stopAltosBluetooth();
}
}
- private synchronized void setState(int s) {
- if (D) Log.d(TAG, "setState(): " + state + " -> " + s);
- state = s;
-
- // This shouldn't be required - mConfigData should be null for any non-connected
- // state, but to be safe and to reduce message size
- AltosConfigData acd = (state == STATE_CONNECTED) ? mConfigData : null;
-
- sendMessageToClients(Message.obtain(null, AltosDroid.MSG_STATE_CHANGE, state, -1, acd));
- }
-
- private void connected() {
+ private void connected() throws InterruptedException {
+ if (D) Log.d(TAG, "connected top");
try {
if (mAltosBluetooth == null)
throw new InterruptedException("no bluetooth");
- mConfigData = mAltosBluetooth.config_data();
- } catch (InterruptedException e) {
+ telemetry_state.config = mAltosBluetooth.config_data();
+ mAltosBluetooth.set_radio_frequency(telemetry_state.frequency);
+ mAltosBluetooth.set_telemetry_rate(telemetry_state.telemetry_rate);
} catch (TimeoutException e) {
// If this timed out, then we really want to retry it, but
// probably safer to just retry the connection from scratch.
return;
}
- setState(STATE_CONNECTED);
+ if (D) Log.d(TAG, "connected bluetooth configured");
+ telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
mTelemetryReader = new TelemetryReader(mAltosBluetooth, mHandler);
mTelemetryReader.start();
-
+
+ if (D) Log.d(TAG, "connected TelemetryReader started");
+
mTelemetryLogger = new TelemetryLogger(this, mAltosBluetooth);
- }
+ if (D) Log.d(TAG, "Notify UI of connection");
+
+ sendMessageToClients();
+ }
private void onTimerTick() {
if (D) Log.d(TAG, "Timer wakeup");
try {
- if (mClients.size() <= 0 && state != STATE_CONNECTED) {
+ if (mClients.size() <= 0 && telemetry_state.connect != TelemetryState.CONNECT_CONNECTED) {
stopSelf();
}
} catch (Throwable t) {
@Override
public void onCreate() {
+ // Get local Bluetooth adapter
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ // If the adapter is null, then Bluetooth is not supported
+ if (mBluetoothAdapter == null) {
+ Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
+ }
+
+ // Initialise preferences
+ AltosDroidPreferences.init(this);
+
+ telemetry_state = new TelemetryState();
+
// Create a reference to the NotificationManager so that we can update our notifcation text later
//mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
- setState(STATE_READY);
+ telemetry_state.connect = TelemetryState.CONNECT_READY;
// Start our timer - first event in 10 seconds, then every 10 seconds after that.
timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 10000L, 10000L);
// Listen for GPS and Network position updates
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
-
+
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
-// locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
+
+ String address = AltosDroidPreferences.active_device();
+ if (address != null)
+ startAltosBluetooth(address);
}
@Override
public void onLocationChanged(Location location) {
- last_location = location;
- sendMessageToClients(Message.obtain(null, AltosDroid.MSG_LOCATION, location));
+ telemetry_state.location = location;
+ if (D) Log.d(TAG, "location changed");
+ sendMessageToClients();
}
public void onStatusChanged(String provider, int status, Bundle extras) {
--- /dev/null
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * 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.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import org.altusmetrum.altoslib_5.*;
+import android.location.Location;
+
+public class TelemetryState {
+ public static final int CONNECT_NONE = 0;
+ public static final int CONNECT_READY = 1;
+ public static final int CONNECT_CONNECTING = 2;
+ public static final int CONNECT_CONNECTED = 3;
+
+ int connect;
+ AltosConfigData config;
+ AltosState state;
+ Location location;
+ int crc_errors;
+ double frequency;
+ int telemetry_rate;
+
+ public TelemetryState() {
+ connect = CONNECT_NONE;
+ config = null;
+ state = null;
+ location = null;
+ crc_errors = 0;
+ frequency = AltosPreferences.frequency(0);
+ telemetry_rate = AltosPreferences.telemetry_rate(0);
+ }
+}
switch (cmd) {
case AltosLib.AO_LOG_FLIGHT:
- state.set_boost_tick(tick);
- state.set_flight(flight());
+ if (state.flight == AltosLib.MISSING) {
+ state.set_boost_tick(tick);
+ state.set_flight(flight());
+ }
/* no place to log start lat/lon yet */
break;
case AltosLib.AO_LOG_GPS_TIME:
import java.io.*;
import java.util.concurrent.*;
-public class AltosFlightReader {
+public abstract class AltosFlightReader {
public String name;
public int serial;
- public void init() { }
+ public void init() {}
- public AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; }
+ public abstract AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException;
- public void close(boolean interrupted) { }
+ public abstract void close(boolean interrupted);
public void set_frequency(double frequency) throws InterruptedException, TimeoutException { }
}
log_file.flush();
file = a;
+ AltosPreferences.set_logfile(link.serial, file);
}
return log_file != null;
}
/* telemetry rate format preference name */
public final static String telemetryRatePreferenceFormat = "RATE-%d";
+ /* log file format preference name */
+ public final static String logfilePreferenceFormat = "LOGFILE-%d";
+
/* voice preference name */
public final static String voicePreference = "VOICE";
/* Telemetry rate (map serial to telemetry format) */
public static Hashtable<Integer, Integer> telemetry_rates;
+ /* Log file (map serial to logfile name) */
+ public static Hashtable<Integer, File> logfiles;
+
/* Voice preference */
public static boolean voice;
public static int launcher_channel;
public static void init(AltosPreferencesBackend in_backend) {
+
+ if (backend != null)
+ return;
+
backend = in_backend;
/* Initialize logdir from preferences */
telemetry_rates = new Hashtable<Integer,Integer>();
+ logfiles = new Hashtable<Integer,File>();
+
voice = backend.getBoolean(voicePreference, true);
callsign = backend.getString(callsignPreference,"N0CALL");
}
}
+ public static void set_logfile(int serial, File new_logfile) {
+ synchronized(backend) {
+ logfiles.put(serial, new_logfile);
+ backend.putString(String.format(logfilePreferenceFormat, serial), new_logfile.getPath());
+ flush_preferences();
+ }
+ }
+
+ public static File logfile(int serial) {
+ synchronized(backend) {
+ if (logfiles.containsKey(serial))
+ return logfiles.get(serial);
+ String logfile_string = backend.getString(String.format(logfilePreferenceFormat, serial), null);
+ if (logfile_string == null)
+ return null;
+ File logfile = new File(logfile_string);
+ logfiles.put(serial, logfile);
+ return logfile;
+ }
+ }
+
public static void set_scanning_telemetry(int new_scanning_telemetry) {
synchronized (backend) {
scanning_telemetry = new_scanning_telemetry;
public class AltosReplayReader extends AltosFlightReader {
Iterator<AltosState> iterator;
File file;
+ boolean real_time;
public AltosState read() {
if (iterator.hasNext())
public void update(AltosState state) throws InterruptedException {
/* Make it run in realtime after the rocket leaves the pad */
- if (state.state > AltosLib.ao_flight_pad && state.time_change > 0)
+ if (real_time && state.state > AltosLib.ao_flight_pad && state.time_change > 0)
Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
state.set_received_time(System.currentTimeMillis());
}
public File backing_file() { return file; }
- public AltosReplayReader(Iterator<AltosState> in_iterator, File in_file) {
+ public AltosReplayReader(Iterator<AltosState> in_iterator, File in_file,
+ boolean in_real_time) {
iterator = in_iterator;
file = in_file;
+ real_time = in_real_time;
name = file.getName();
}
+
+ public AltosReplayReader(Iterator<AltosState> in_iterator, File in_file) {
+ this(in_iterator, in_file, false);
+ }
}
}
public abstract void write(PrintStream out);
+
+ public static AltosStateIterable iterable(File file) {
+ FileInputStream in;
+ try {
+ in = new FileInputStream(file);
+ } catch (Exception e) {
+ System.out.printf("Failed to open file '%s'\n", file);
+ return null;
+ }
+ if (file.getName().endsWith("telem"))
+ return new AltosTelemetryFile(in);
+ else
+ return new AltosEepromFile(in);
+ }
}
public AltosTelemetryMegaData(int[] bytes) {
super(bytes);
- state = int8(5);
+ state = uint8(5);
v_batt = int16(6);
v_pyro = int16(8);
sense = new int[6];
for (int i = 0; i < 6; i++) {
- sense[i] = int8(10 + i) << 4;
+ sense[i] = uint8(10 + i) << 4;
sense[i] |= sense[i] >> 8;
}
int telemetry;
int telemetry_rate;
AltosState state = null;
+ AltosFlightReader stacked;
LinkedBlockingQueue<AltosLine> telem;
public AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException {
+ if (stacked != null) {
+ state = stacked.read();
+ if (state != null)
+ return state;
+ stacked = null;
+ }
AltosLine l = telem.take();
if (l.line == null)
throw new IOException("IO error");
}
public void close(boolean interrupted) {
+
+ if (stacked != null) {
+ stacked.close(interrupted);
+ stacked = null;
+ }
+
link.remove_monitor(telem);
log.close();
try {
return link.monitor_battery();
}
- public AltosTelemetryReader (AltosLink in_link)
+ public AltosTelemetryReader (AltosLink in_link, AltosFlightReader in_stacked)
throws IOException, InterruptedException, TimeoutException {
link = in_link;
+ stacked = in_stacked;
boolean success = false;
try {
log = new AltosLog(link);
close(true);
}
}
+
+ private static AltosFlightReader existing_data(AltosLink link) {
+ if (link == null)
+ return null;
+
+ File file = AltosPreferences.logfile(link.serial);
+ if (file != null) {
+ AltosStateIterable iterable = AltosStateIterable.iterable(file);
+ if (iterable != null)
+ return new AltosReplayReader(iterable.iterator(), file, false);
+ }
+ return null;
+ }
+
+ public AltosTelemetryReader(AltosLink link)
+ throws IOException, InterruptedException, TimeoutException {
+ this(link, existing_data(link));
+ }
}
"0.050", "0.100", "0.250", "0.500", "1.0", "2.0"
};
+ boolean initializing;
+
public void set_pyro_firing_time(double new_pyro_firing_time) {
+ initializing = true;
pyro_firing_time_value.setSelectedItem(Double.toString(new_pyro_firing_time));
pyro_firing_time_value.setEnabled(new_pyro_firing_time >= 0);
+ initializing = false;
}
public double get_pyro_firing_time() throws AltosConfigDataException {
}
public void set_dirty() {
- owner.set_dirty();
+ if (!initializing)
+ owner.set_dirty();
}
public void itemStateChanged(ItemEvent e) {
- owner.set_dirty();
+ if (!initializing)
+ owner.set_dirty();
}
public void changedUpdate(DocumentEvent e) {
- owner.set_dirty();
+ if (!initializing)
+ owner.set_dirty();
}
public void insertUpdate(DocumentEvent e) {
- owner.set_dirty();
+ if (!initializing)
+ owner.set_dirty();
}
public void removeUpdate(DocumentEvent e) {
- owner.set_dirty();
+ if (!initializing)
+ owner.set_dirty();
}
public void units_changed(boolean imperial_units) {
if (!config_version.get().equals("0.0"))
break;
been_there = true;
- config.serial_line.printf("C\n ");
- config.serial_line.flush_input();
+ if (config != null && config.serial_line != null) {
+ config.serial_line.printf("C\n ");
+ config.serial_line.flush_input();
+ }
}
} catch (InterruptedException ie) {
}
}
void abort() {
- serial_line.close();
- serial_line = null;
+ if (serial_line != null) {
+ serial_line.close();
+ serial_line = null;
+ }
JOptionPane.showMessageDialog(owner,
String.format("Connection to \"%s\" failed",
device.toShortString()),
}
}
+ boolean report_pad(AltosState state) {
+ if ((state.state == AltosLib.ao_flight_stateless ||
+ state.state < AltosLib.ao_flight_pad) &&
+ state.gps != null &&
+ state.gps.lat != AltosLib.MISSING)
+ {
+ return false;
+ }
+ return true;
+ }
+
class PadLat extends AltosUIIndicator {
double last_lat = AltosLib.MISSING - 1;
String label = null;
if (state != null) {
- if (state.state < AltosLib.ao_flight_pad && state.gps != null && state.gps.lat != AltosLib.MISSING) {
- lat = state.gps.lat;
- label = "Latitude";
- } else {
+ if (report_pad(state)) {
lat = state.pad_lat;
label = "Pad Latitude";
+ } else {
+ lat = state.gps.lat;
+ label = "Latitude";
}
}
if (lat != last_lat) {
String label = null;
if (state != null) {
- if (state.state < AltosLib.ao_flight_pad && state.gps != null && state.gps.lon != AltosLib.MISSING) {
- lon = state.gps.lon;
- label = "Longitude";
- } else {
+ if (report_pad(state)) {
lon = state.pad_lon;
label = "Pad Longitude";
+ } else {
+ lon = state.gps.lon;
+ label = "Longitude";
}
}
if (lon != last_lon) {
String label = null;
if (state != null) {
- if (state.state < AltosLib.ao_flight_pad && state.gps != null && state.gps.alt != AltosLib.MISSING) {
- alt = state.gps.alt;
- label = "Altitude";
- } else {
+ if (report_pad(state)) {
alt = state.pad_alt;
label = "Pad Altitude";
+ } else {
+ alt = state.gps.alt;
+ label = "Altitude";
}
}
if (alt != last_alt) {
--- /dev/null
+#!/bin/sh
+
+VERSION=1.0
+PRODUCT=EasyMega
+BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
+
+echo "$PRODUCT-v$VERSION Test Program"
+echo "Copyright 2014 by Keith Packard. Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\t$PRODUCT v$VERSION powered from USB"
+echo
+
+ret=1
+ao-list | while read product serial dev; do
+ case "$product" in
+ "$PRODUCT-v$VERSION")
+
+ echo "Testing $product $serial $dev"
+
+ for igniter in drogue main 0 1 2 3; do
+ echo "Testing $igniter igniter."
+ echo -n "Press enter to continue..."
+ read foo < /dev/tty
+ ../ao-tools/ao-test-igniter/ao-test-igniter --tty="$dev" $igniter
+
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "failed"
+ exit 1
+ ;;
+ esac
+ done
+
+ echo "Testing baro sensor"
+ ../ao-tools/ao-test-baro/ao-test-baro --tty="$dev"
+
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "failed"
+ exit 1
+ esac
+
+ FLASHSIZE=8388608
+
+ echo "Testing flash"
+ ../ao-tools/ao-test-flash/ao-test-flash --tty="$dev" "$FLASHSIZE"
+
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "failed"
+ exit 1
+ esac
+
+ echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship
+ ret=0
+ ;;
+ *)
+ echo "Skipping $product $serial $dev"
+ ;;
+ esac
+done
echo 'E 0' > $dev
-./cal-accel $dev
+../ao-tools/ao-cal-accel/ao-cal-accel $dev
echo 'E 1' > $dev
+
+./test-easymega
+
+exit $?
SERIAL=$SERIAL ./cal-freq $dev
-./cal-accel $dev
+../ao-tools/ao-cal-accel/ao-cal-accel $dev
echo 'E 1' > $dev
echo "Copyright 2014 by Bdale Garbee. Released under GPL v2"
echo
echo "Expectations:"
-echo "\tTeleMetrum v$VERSIOn powered from USB"
+echo "\tTeleMetrum v$VERSION powered from USB"
echo "\t\twith ST-Link-V2 cabled to debug header"
echo "\t\twith coax from UHF to frequency counter"
echo
SERIAL=$SERIAL ./cal-freq $dev
-./cal-accel $dev
+../ao-tools/ao-cal-accel/ao-cal-accel $dev
echo 'E 1' > $dev
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-test-flash ao-cal-accel
if LIBSTLINK
SUBDIRS += ao-stmload
endif
--- /dev/null
+ao-cal-accel
--- /dev/null
+bin_PROGRAMS=ao-cal-accel
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+
+ao_cal_accel_DEPENDENCIES = $(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_cal_accel_LDADD=$(top_builddir)/ao-tools/lib/libao-tools.a $(LIBUSB_LIBS)
+
+ao_cal_accel_SOURCES=ao-cal-accel.c
+
+man_MANS = ao-cal-accel.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-accel" ""
+.SH NAME
+ao-cal-accel \- Calibrate AltOS flight computer accelerometers
+.SH SYNOPSIS
+.B "ao-cal-accel"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+.SH DESCRIPTION
+.I ao-cal-accel
+drives the built-in accelerometer calibration and validates the results.
+.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-accel
+opens the target device, executes the accelerometer calibration
+command, verifies that it executed correctly, 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 "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 **accel;
+ char line[1024];
+ int l;
+ int running = 0;
+ int worked = 1;
+
+ cc_usb_printf(usb, "E 1\nc a 0\n");
+
+ for (;;) {
+ int c = cc_usb_getchar_timeout(usb, 20*1000);
+
+ if (c == '\n')
+ l = 0;
+ else if (l < sizeof (line) - 1)
+ line[l++] = c;
+ line[l] = '\0';
+ putchar(c); fflush(stdout);
+ if (strstr(line, "press a key...")) {
+ await_key();
+ cc_usb_printf(usb, " ");
+ l = 0;
+ running = 1;
+ }
+ else if (strstr(line, "Invalid"))
+ worked = 0;
+ if (running && strstr(line, ">")) {
+ printf("\n");
+ break;
+ }
+ }
+ cc_usb_printf(usb, "E 0\n");
+
+ if (!worked) {
+ printf("Calibration failed\n");
+ return 0;
+ }
+
+ b = flash(usb);
+
+ accel = find_flash(b, "Accel cal");
+ if (!accel) {
+ printf("no response\n");
+ return 0;
+ }
+
+ printf ("Accel cal +1g: %s -1g: %s\n",
+ accel[3], accel[5]);
+
+ printf ("Saving..."); fflush(stdout);
+ cc_usb_printf (usb, "c w\n");
+ cc_usb_sync(usb);
+ b = flash(usb);
+ printf ("done\n");
+
+ return worked;
+}
+
+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-igniter/Makefile
ao-tools/ao-test-baro/Makefile
ao-tools/ao-test-flash/Makefile
+ao-tools/ao-cal-accel/Makefile
ao-utils/Makefile
src/Version
])
void
ao_pad_manual(void)
{
+ uint8_t ignite;
+ int repeat;
ao_cmd_white();
if (!ao_match_word("DoIt"))
return;
ao_cmd_decimal();
if (ao_cmd_status != ao_cmd_success)
return;
- ao_pad_ignite = 1 << ao_cmd_lex_i;
- ao_wakeup(&ao_pad_ignite);
+ ignite = 1 << ao_cmd_lex_i;
+ ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success) {
+ repeat = 1;
+ ao_cmd_status = ao_cmd_success;
+ } else
+ repeat = ao_cmd_lex_i;
+ while (repeat-- > 0) {
+ ao_pad_ignite = ignite;
+ ao_wakeup(&ao_pad_ignite);
+ ao_delay(AO_PAD_FIRE_TIME>>1);
+ }
}
static __xdata struct ao_task ao_pad_task;
ao_flight_number = 1;
}
-void
+uint8_t
ao_log_scan(void) __reentrant
{
uint8_t log_slot;
uint8_t log_slots;
-#if !FLIGHT_LOG_APPEND
+#if FLIGHT_LOG_APPEND
+ uint8_t ret;
+#else
uint8_t log_want;
#endif
empty = ao_log_current_pos;
}
}
+ ret = 1;
} else {
ao_log_find_max_erase_flight();
+ ret = 0;
}
+ ao_wakeup(&ao_flight_number);
+ return ret;
#else
if (ao_flight_number)
if (++log_slot >= log_slots)
log_slot = 0;
} while (log_slot != log_want);
-#endif
ao_wakeup(&ao_flight_number);
+ return 0;
+#endif
}
void
/* functions provided in ao_log.c */
/* Figure out the current flight number */
-void
+uint8_t
ao_log_scan(void) __reentrant;
/* Return the position of the start of the given log slot */
struct ao_pyro *pyro;
uint8_t p, any_waiting;
uint16_t fire = 0;
-
+
any_waiting = 0;
for (p = 0; p < AO_PYRO_NUM; p++) {
pyro = &ao_config.pyro[p];
* the delay to expire
*/
if (pyro->delay_done) {
+
+ /* Check to make sure the required conditions
+ * remain valid. If not, inhibit the channel
+ * by setting the fired bit
+ */
+ if (!ao_pyro_ready(pyro)) {
+ pyro->fired = 1;
+ continue;
+ }
+
if ((int16_t) (ao_time() - pyro->delay_done) < 0)
continue;
}
printf ("invalid pyro channel %d\n", p);
return;
}
- pyro_tmp.flags = 0;
+ memset(&pyro_tmp, '\0', sizeof (pyro_tmp));
for (;;) {
ao_cmd_white();
if (ao_cmd_lex_c == '\n')
}
pyro_tmp.flags |= ao_pyro_values[v].flag;
if (ao_pyro_values[v].offset != NO_VALUE) {
+ uint8_t negative = 0;
+ ao_cmd_white();
+ if (ao_cmd_lex_c == '-') {
+ negative = 1;
+ ao_cmd_lex();
+ }
ao_cmd_decimal();
if (ao_cmd_status != ao_cmd_success)
return;
- if (ao_pyro_values[v].flag & AO_PYRO_8_BIT_VALUE)
+ if (ao_pyro_values[v].flag & AO_PYRO_8_BIT_VALUE) {
+ if (negative) {
+ ao_cmd_status = ao_cmd_syntax_error;
+ return;
+ }
*((uint8_t *) ((char *) &pyro_tmp + ao_pyro_values[v].offset)) = ao_cmd_lex_i;
- else
+ } else {
+ if (negative)
+ ao_cmd_lex_i = -ao_cmd_lex_i;
*((int16_t *) ((char *) &pyro_tmp + ao_pyro_values[v].offset)) = ao_cmd_lex_i;
+ }
}
}
_ao_config_edit_start();
#if !HAS_USB_CONNECT
ao_tracker_force_telem = 1;
#endif
- ao_log_scan();
+ log_started = ao_log_scan();
ao_rdf_set(1);
ao_tracker_erase_end(void)
{
if (erasing_current) {
- ao_log_scan();
- log_started = 0;
+ log_started = ao_log_scan();
ao_mutex_put(&tracker_mutex);
}
}