diff options
| author | Bdale Garbee <bdale@gag.com> | 2014-09-06 13:41:36 -0600 | 
|---|---|---|
| committer | Bdale Garbee <bdale@gag.com> | 2014-09-06 13:41:36 -0600 | 
| commit | 8c212cd5bfa03f71a31d84bd0051314e77d88461 (patch) | |
| tree | 5be036b3510b8b474ad829caea20fcbc75b56839 | |
| parent | e9714e34091abe657aa1b30aeda9466331aa39c1 (diff) | |
| parent | dd26ec2e706bdd29090759deeb90090a0e3b74f0 (diff) | |
Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
Conflicts:
	ao-bringup/turnon_telemega
45 files changed, 1288 insertions, 376 deletions
| diff --git a/altosdroid/Notebook b/altosdroid/Notebook index ebb3578d..5a4df032 100644 --- a/altosdroid/Notebook +++ b/altosdroid/Notebook @@ -8,6 +8,8 @@ Desired AltosDroid feature list   *) 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, @@ -23,10 +25,16 @@ Desired AltosDroid feature list   *) 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 diff --git a/altosdroid/res/layout/device_list.xml b/altosdroid/res/layout/device_list.xml index 395695f8..93d65517 100644 --- a/altosdroid/res/layout/device_list.xml +++ b/altosdroid/res/layout/device_list.xml @@ -18,39 +18,39 @@      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> diff --git a/altosdroid/res/menu/option_menu.xml b/altosdroid/res/menu/option_menu.xml index ee9d475f..3bd5a54e 100644 --- a/altosdroid/res/menu/option_menu.xml +++ b/altosdroid/res/menu/option_menu.xml @@ -17,10 +17,16 @@      <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> diff --git a/altosdroid/res/values/strings.xml b/altosdroid/res/values/strings.xml index ce335b76..0cc99349 100644 --- a/altosdroid/res/values/strings.xml +++ b/altosdroid/res/values/strings.xml @@ -22,13 +22,15 @@  	<!-- 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> diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java index 484efaf8..51ef5e94 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java @@ -52,6 +52,7 @@ public class AltosBluetooth extends AltosLink {  	// Constructor  	public AltosBluetooth(BluetoothDevice in_device, Handler in_handler) { +//		set_debug(D);  		adapter = BluetoothAdapter.getDefaultAdapter();  		device = in_device;  		handler = in_handler; @@ -136,9 +137,27 @@ public class AltosBluetooth extends AltosLink {  		}  	} +	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();  		}  	} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index c9c38d98..f6cceac9 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -44,32 +44,34 @@ import android.view.Menu;  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; @@ -78,12 +80,16 @@ public class AltosDroid extends FragmentActivity {  	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; @@ -92,23 +98,14 @@ public class AltosDroid extends FragmentActivity {  	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; @@ -120,39 +117,21 @@ public class AltosDroid extends FragmentActivity {  		@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;  			}  		} @@ -208,10 +187,65 @@ public class AltosDroid extends FragmentActivity {  		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) { @@ -226,42 +260,73 @@ public class AltosDroid extends FragmentActivity {  		}  	} -	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); @@ -276,7 +341,12 @@ public class AltosDroid extends FragmentActivity {  			}  			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"); @@ -285,10 +355,10 @@ public class AltosDroid extends FragmentActivity {  		}  		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;  	} @@ -330,8 +400,6 @@ public class AltosDroid extends FragmentActivity {  		super.onCreate(savedInstanceState);  		if(D) Log.e(TAG, "+++ ON CREATE +++"); -		fm = getSupportFragmentManager(); -  		// Get local Bluetooth adapter  		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); @@ -339,12 +407,9 @@ public class AltosDroid extends FragmentActivity {  		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); @@ -387,7 +452,6 @@ public class AltosDroid extends FragmentActivity {  		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); @@ -403,11 +467,10 @@ public class AltosDroid extends FragmentActivity {  		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);  	} @@ -416,15 +479,16 @@ public class AltosDroid extends FragmentActivity {  		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 @@ -453,6 +517,7 @@ public class AltosDroid extends FragmentActivity {  		if(D) Log.e(TAG, "--- ON DESTROY ---");  		if (mAltosVoice != null) mAltosVoice.stop(); +		stop_timer();  	}  	public void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -480,19 +545,21 @@ public class AltosDroid extends FragmentActivity {  		}  	} -	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(); @@ -550,6 +617,11 @@ public class AltosDroid extends FragmentActivity {  			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 @@ -597,6 +669,10 @@ public class AltosDroid extends FragmentActivity {  			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;  	} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java index 70d7def8..9cef1319 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java @@ -1,5 +1,5 @@  /* - * 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 @@ -14,88 +14,38 @@   * 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; +		}  	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java new file mode 100644 index 00000000..be41ae7c --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java @@ -0,0 +1,101 @@ +/* + * 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(); +	} +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java index b960eb1a..8e625da6 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -27,8 +27,9 @@ import android.support.v4.app.FragmentTransaction;  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; @@ -37,39 +38,46 @@ public abstract class AltosDroidTab extends Fragment {  	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); -		} -	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java index b05913b6..969992d3 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java @@ -58,7 +58,7 @@ public class AltosVoice {  		}  	} -	public void tell(AltosState state) { +	public void tell(AltosState state, AltosGreatCircle from_receiver) {  		if (!tts_enabled) return;  		boolean	spoke = false; @@ -68,12 +68,14 @@ public class AltosVoice {  			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;  			}  		} @@ -88,13 +90,14 @@ public class AltosVoice {  		}  		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; @@ -112,29 +115,30 @@ public class AltosVoice {  				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;  			} @@ -153,9 +157,9 @@ public class AltosVoice {  				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;  			}  		} @@ -186,9 +190,10 @@ public class AltosVoice {  			}  		} -		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(); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java index 8e8d9c03..267c90f8 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java @@ -20,6 +20,7 @@ package org.altusmetrum.AltosDroid;  import android.content.res.Resources;  import android.graphics.drawable.Drawable;  import android.widget.ImageView; +import android.view.View;  public class GoNoGoLights {  	private Boolean state; @@ -51,14 +52,27 @@ public class GoNoGoLights {  		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); +	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java index c146c277..fa4e3c8b 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java @@ -91,12 +91,13 @@ public class TabAscent extends AltosDroidTab {  	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")); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java index 6d781efd..28068666 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java @@ -93,14 +93,14 @@ public class TabDescent extends AltosDroidTab {  	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>"); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java index 16427d8b..b257b936 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java @@ -78,7 +78,7 @@ public class TabLanded extends AltosDroidTab {  	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")); @@ -91,9 +91,9 @@ public class TabLanded extends AltosDroidTab {  		}  		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());  		}  	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index 811e5482..ab338ac2 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -158,7 +158,7 @@ public class TabMap extends AltosDroidTab {  	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) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java index 03b78b75..32df71d7 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java @@ -33,10 +33,13 @@ public class TabPad extends AltosDroidTab {  	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; @@ -59,16 +62,19 @@ public class TabPad extends AltosDroidTab {  	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()); @@ -107,11 +113,23 @@ public class TabPad extends AltosDroidTab {  		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) { @@ -141,12 +159,12 @@ public class TabPad extends AltosDroidTab {  		}  		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);  		}  	} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java index 3ba5afa9..971c3e80 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java @@ -31,6 +31,7 @@ import org.altusmetrum.altoslib_5.*;  public class TelemetryReader extends Thread {  	private static final String TAG = "TelemetryReader"; +	private static final boolean D = true;  	int         crc_errors; @@ -39,6 +40,8 @@ public class TelemetryReader extends Thread {  	AltosLink   link;  	AltosState  state = null; +	AltosFlightReader	stacked; +  	LinkedBlockingQueue<AltosLine> telemQueue;  	public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException { @@ -56,6 +59,10 @@ public class TelemetryReader extends Thread {  	public void close() {  		state = null; +		if (stacked != null) { +			stacked.close(false); +			stacked = null; +		}  		link.remove_monitor(telemQueue);  		link = null;  		telemQueue.clear(); @@ -66,7 +73,27 @@ public class TelemetryReader extends Thread {  		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(); @@ -84,12 +111,34 @@ public class TelemetryReader extends Thread {  		}  	} -	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));  	}  } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index 4ec353e3..30d94409 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -28,6 +28,7 @@ import android.app.Notification;  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; @@ -63,11 +64,6 @@ public class TelemetryService extends Service implements LocationListener {  	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; @@ -81,21 +77,17 @@ public class TelemetryService extends Service implements LocationListener {  	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; @@ -104,17 +96,15 @@ public class TelemetryService extends Service implements LocationListener {  		@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);  				} @@ -126,47 +116,59 @@ public class TelemetryService extends Service implements LocationListener {  				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); @@ -174,9 +176,18 @@ public class TelemetryService extends Service implements LocationListener {  		}  	} -	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); @@ -186,7 +197,7 @@ public class TelemetryService extends Service implements LocationListener {  	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(); @@ -206,18 +217,21 @@ public class TelemetryService extends Service implements LocationListener {  			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 @@ -225,28 +239,19 @@ public class TelemetryService extends Service implements LocationListener {  			// ... 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. @@ -254,19 +259,25 @@ public class TelemetryService extends Service implements LocationListener {  			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) { @@ -277,19 +288,35 @@ public class TelemetryService extends Service implements LocationListener {  	@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 @@ -345,8 +372,9 @@ public class TelemetryService extends Service implements LocationListener {  	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) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java new file mode 100644 index 00000000..d3341739 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java @@ -0,0 +1,46 @@ +/* + * 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); +	} +} diff --git a/altoslib/AltosEepromGPS.java b/altoslib/AltosEepromGPS.java index 482f0b5f..8c991a6e 100644 --- a/altoslib/AltosEepromGPS.java +++ b/altoslib/AltosEepromGPS.java @@ -91,8 +91,10 @@ public class AltosEepromGPS extends AltosEeprom {  		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: diff --git a/altoslib/AltosFlightReader.java b/altoslib/AltosFlightReader.java index c0565e88..be103838 100644 --- a/altoslib/AltosFlightReader.java +++ b/altoslib/AltosFlightReader.java @@ -21,16 +21,16 @@ import java.text.*;  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 { } diff --git a/altoslib/AltosLog.java b/altoslib/AltosLog.java index 1cac6b52..28116e3d 100644 --- a/altoslib/AltosLog.java +++ b/altoslib/AltosLog.java @@ -72,6 +72,7 @@ public class AltosLog implements Runnable {  			}  			log_file.flush();  			file = a; +			AltosPreferences.set_logfile(link.serial, file);  		}  		return log_file != null;  	} diff --git a/altoslib/AltosPreferences.java b/altoslib/AltosPreferences.java index 72cfeb4b..dba57dcb 100644 --- a/altoslib/AltosPreferences.java +++ b/altoslib/AltosPreferences.java @@ -38,6 +38,9 @@ public class AltosPreferences {  	/* 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"; @@ -83,6 +86,9 @@ public class AltosPreferences {  	/* 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; @@ -151,6 +157,10 @@ public class AltosPreferences {  	public static int launcher_channel;  	public static void init(AltosPreferencesBackend in_backend) { + +		if (backend != null) +			return; +  		backend = in_backend;  		/* Initialize logdir from preferences */ @@ -172,6 +182,8 @@ public class AltosPreferences {  		telemetry_rates = new Hashtable<Integer,Integer>(); +		logfiles = new Hashtable<Integer,File>(); +  		voice = backend.getBoolean(voicePreference, true);  		callsign = backend.getString(callsignPreference,"N0CALL"); @@ -300,6 +312,27 @@ public class AltosPreferences {  		}  	} +	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; diff --git a/altoslib/AltosReplayReader.java b/altoslib/AltosReplayReader.java index 15093af1..2864e02a 100644 --- a/altoslib/AltosReplayReader.java +++ b/altoslib/AltosReplayReader.java @@ -27,6 +27,7 @@ import java.util.*;  public class AltosReplayReader extends AltosFlightReader {  	Iterator<AltosState>	iterator;  	File	file; +	boolean real_time;  	public AltosState read() {  		if (iterator.hasNext()) @@ -39,16 +40,22 @@ public class AltosReplayReader extends AltosFlightReader {  	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); +	}  } diff --git a/altoslib/AltosStateIterable.java b/altoslib/AltosStateIterable.java index f7cd424d..4154b71c 100644 --- a/altoslib/AltosStateIterable.java +++ b/altoslib/AltosStateIterable.java @@ -26,4 +26,18 @@ public abstract class AltosStateIterable implements Iterable<AltosState> {  	}  	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); +	}  } diff --git a/altoslib/AltosTelemetryMegaData.java b/altoslib/AltosTelemetryMegaData.java index 8b1869bb..d949c02f 100644 --- a/altoslib/AltosTelemetryMegaData.java +++ b/altoslib/AltosTelemetryMegaData.java @@ -36,7 +36,7 @@ public class AltosTelemetryMegaData extends AltosTelemetryStandard {  	public AltosTelemetryMegaData(int[] bytes) {  		super(bytes); -		state = int8(5); +		state = uint8(5);  		v_batt = int16(6);  		v_pyro = int16(8); @@ -44,7 +44,7 @@ public class AltosTelemetryMegaData extends AltosTelemetryStandard {  		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;  		} diff --git a/altoslib/AltosTelemetryReader.java b/altoslib/AltosTelemetryReader.java index 5ed50134..7539452d 100644 --- a/altoslib/AltosTelemetryReader.java +++ b/altoslib/AltosTelemetryReader.java @@ -28,10 +28,17 @@ public class AltosTelemetryReader extends AltosFlightReader {  	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"); @@ -53,6 +60,12 @@ public class AltosTelemetryReader extends AltosFlightReader {  	}  	public void close(boolean interrupted) { + +		if (stacked != null) { +			stacked.close(interrupted); +			stacked = null; +		} +  		link.remove_monitor(telem);  		log.close();  		try { @@ -148,9 +161,10 @@ public class AltosTelemetryReader extends AltosFlightReader {  		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); @@ -169,4 +183,22 @@ public class AltosTelemetryReader extends AltosFlightReader {  				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)); +	}  } diff --git a/altosui/AltosConfigPyroUI.java b/altosui/AltosConfigPyroUI.java index dd4fb505..64336c8e 100644 --- a/altosui/AltosConfigPyroUI.java +++ b/altosui/AltosConfigPyroUI.java @@ -285,9 +285,13 @@ public class AltosConfigPyroUI  		"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 { @@ -301,23 +305,28 @@ public class AltosConfigPyroUI  	}  	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) { diff --git a/altosui/AltosConfigTD.java b/altosui/AltosConfigTD.java index 4389e49b..9020ed9d 100644 --- a/altosui/AltosConfigTD.java +++ b/altosui/AltosConfigTD.java @@ -223,8 +223,10 @@ public class AltosConfigTD implements ActionListener {  					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) {  			} @@ -277,8 +279,10 @@ public class AltosConfigTD implements ActionListener {  	}  	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()), diff --git a/altosui/AltosPad.java b/altosui/AltosPad.java index 5c33fd16..eb0c5644 100644 --- a/altosui/AltosPad.java +++ b/altosui/AltosPad.java @@ -117,6 +117,17 @@ public class AltosPad extends AltosUIFlightTab {  		}  	} +	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; @@ -126,12 +137,12 @@ public class AltosPad extends AltosUIFlightTab {  			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) { @@ -163,12 +174,12 @@ public class AltosPad extends AltosUIFlightTab {  			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) { @@ -200,12 +211,12 @@ public class AltosPad extends AltosUIFlightTab {  			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) { diff --git a/ao-bringup/test-easymega b/ao-bringup/test-easymega new file mode 100755 index 00000000..eabe1ee5 --- /dev/null +++ b/ao-bringup/test-easymega @@ -0,0 +1,68 @@ +#!/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 diff --git a/ao-bringup/turnon_easymega b/ao-bringup/turnon_easymega index 7d23f5f7..b313e162 100755 --- a/ao-bringup/turnon_easymega +++ b/ao-bringup/turnon_easymega @@ -52,6 +52,10 @@ esac  echo 'E 0' > $dev -./cal-accel $dev +../ao-tools/ao-cal-accel/ao-cal-accel $dev  echo 'E 1' > $dev + +./test-easymega + +exit $? diff --git a/ao-bringup/turnon_telemega b/ao-bringup/turnon_telemega index 46c254b6..7745a8e5 100755 --- a/ao-bringup/turnon_telemega +++ b/ao-bringup/turnon_telemega @@ -54,6 +54,6 @@ echo 'E 0' > $dev  SERIAL=$SERIAL ./cal-freq $dev -./cal-accel $dev +../ao-tools/ao-cal-accel/ao-cal-accel $dev  echo 'E 1' > $dev diff --git a/ao-bringup/turnon_telemetrum b/ao-bringup/turnon_telemetrum index c6e7d1cc..48ff1e27 100755 --- a/ao-bringup/turnon_telemetrum +++ b/ao-bringup/turnon_telemetrum @@ -20,7 +20,7 @@ echo "TeleMetrum v$VERSION Turn-On and Calibration Program"  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 @@ -53,6 +53,6 @@ echo 'E 0' > $dev  SERIAL=$SERIAL ./cal-freq $dev -./cal-accel $dev +../ao-tools/ao-cal-accel/ao-cal-accel $dev  echo 'E 1' > $dev diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index 4526f514..2d873e50 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -2,7 +2,7 @@ SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list \  	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 diff --git a/ao-tools/ao-cal-accel/.gitignore b/ao-tools/ao-cal-accel/.gitignore new file mode 100644 index 00000000..73402b2b --- /dev/null +++ b/ao-tools/ao-cal-accel/.gitignore @@ -0,0 +1 @@ +ao-cal-accel diff --git a/ao-tools/ao-cal-accel/Makefile.am b/ao-tools/ao-cal-accel/Makefile.am new file mode 100644 index 00000000..d278097a --- /dev/null +++ b/ao-tools/ao-cal-accel/Makefile.am @@ -0,0 +1,11 @@ +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 diff --git a/ao-tools/ao-cal-accel/ao-cal-accel.1 b/ao-tools/ao-cal-accel/ao-cal-accel.1 new file mode 100644 index 00000000..eb75d7c7 --- /dev/null +++ b/ao-tools/ao-cal-accel/ao-cal-accel.1 @@ -0,0 +1,58 @@ +.\" +.\" 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 diff --git a/ao-tools/ao-cal-accel/ao-cal-accel.c b/ao-tools/ao-cal-accel/ao-cal-accel.c new file mode 100644 index 00000000..9a988648 --- /dev/null +++ b/ao-tools/ao-cal-accel/ao-cal-accel.c @@ -0,0 +1,284 @@ +/* + * 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); +} diff --git a/configure.ac b/configure.ac index 38bab8e1..683da846 100644 --- a/configure.ac +++ b/configure.ac @@ -537,6 +537,7 @@ ao-tools/ao-flash/Makefile  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  ]) diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index 144cbd70..dc2c83fe 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -362,14 +362,26 @@ ao_pad_test(void)  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; diff --git a/src/kernel/ao_log.c b/src/kernel/ao_log.c index dc3b6486..40a96ef7 100644 --- a/src/kernel/ao_log.c +++ b/src/kernel/ao_log.c @@ -192,12 +192,14 @@ ao_log_find_max_erase_flight(void) __reentrant  		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 @@ -248,9 +250,13 @@ ao_log_scan(void) __reentrant  				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) @@ -278,8 +284,9 @@ ao_log_scan(void) __reentrant  		if (++log_slot >= log_slots)  			log_slot = 0;  	} while (log_slot != log_want); -#endif  	ao_wakeup(&ao_flight_number); +	return 0; +#endif  }  void diff --git a/src/kernel/ao_log.h b/src/kernel/ao_log.h index c5fa7fab..c13a2580 100644 --- a/src/kernel/ao_log.h +++ b/src/kernel/ao_log.h @@ -72,7 +72,7 @@ ao_log(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 */ diff --git a/src/kernel/ao_pyro.c b/src/kernel/ao_pyro.c index 0b286466..3044d565 100644 --- a/src/kernel/ao_pyro.c +++ b/src/kernel/ao_pyro.c @@ -252,7 +252,7 @@ ao_pyro_check(void)  	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]; @@ -288,6 +288,16 @@ ao_pyro_check(void)  		 * 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;  		} @@ -465,7 +475,7 @@ ao_pyro_set(void)  		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') @@ -489,13 +499,26 @@ ao_pyro_set(void)  		}  		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(); diff --git a/src/kernel/ao_tracker.c b/src/kernel/ao_tracker.c index d9434048..9b007af8 100644 --- a/src/kernel/ao_tracker.c +++ b/src/kernel/ao_tracker.c @@ -72,7 +72,7 @@ ao_tracker(void)  #if !HAS_USB_CONNECT  	ao_tracker_force_telem = 1;  #endif -	ao_log_scan(); +	log_started = ao_log_scan();  	ao_rdf_set(1); @@ -181,8 +181,7 @@ void  ao_tracker_erase_end(void)  {  	if (erasing_current) { -		ao_log_scan(); -		log_started = 0; +		log_started = ao_log_scan();  		ao_mutex_put(&tracker_mutex);  	}  } | 
