From: Robert Garbee Date: Sat, 13 Jun 2015 23:40:59 +0000 (-0600) Subject: Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos X-Git-Tag: 1.6.0.3~41 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=c46c2c5767c6e909fa58587e6c864a4fbaa9fa20;hp=39f4361675aa13899864f427a33d4aa48be56cd2 Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos --- diff --git a/altosdroid/Notebook b/altosdroid/Notebook index 6a246df7..38a4bc01 100644 --- a/altosdroid/Notebook +++ b/altosdroid/Notebook @@ -15,17 +15,39 @@ Desired AltosDroid feature list *) Monitor-idle mode - *) Frequency scanning + *) TeleBT battery voltage - *) Select satellite imaging mode + *) Select tracker by clicking map - *) TeleBT battery voltage + *) Auto select tracker after long delay - *) Deal with long bluetooth list. Currently, a list longer than - the screen makes it impossible to use entries off the bottom. + *) Evaluate performance issues - *) Pickle/unpickle state instead of reloading entire history from - file. Current restart time is lengthy. + *) Merge offline/online maps into single tab with mode + + *) Make voice responses depend on selected tab + + *) Monitor TeleMega igniters + + *) Convert to four tab design: + + 1) Pad + + Report out GPS status, report igniter/battery status changes + + 2) Flight + + Report height, speed, az/el/range + + 3) Recovery + + Report range bearing/heading (bearing if stationary, + heading if in motion + + 4) Map + + Pick out report based on current flight status + (presume flight if in motion, recovery otherwise) Completed features @@ -47,3 +69,28 @@ Completed features Done + *) Select satellite imaging mode + + Done + + *) Deal with long bluetooth list. Currently, a list longer than + the screen makes it impossible to use entries off the bottom. + + Done + + *) Pickle/unpickle state instead of reloading entire history from + file. Current restart time is lengthy. + + Done + + *) Offline maps + + Done + + *) Multi-tracker management + + Done + + *) Provide units for age field, turn red if old + + Done diff --git a/altosdroid/res/menu/option_menu.xml b/altosdroid/res/menu/option_menu.xml index 2109ae04..9cb57dfc 100644 --- a/altosdroid/res/menu/option_menu.xml +++ b/altosdroid/res/menu/option_menu.xml @@ -22,9 +22,6 @@ - @@ -40,4 +37,13 @@ + + + diff --git a/altosdroid/res/values/Colors.xml b/altosdroid/res/values/Colors.xml new file mode 100644 index 00000000..055c6df2 --- /dev/null +++ b/altosdroid/res/values/Colors.xml @@ -0,0 +1,20 @@ + + + + #ffff4040 + diff --git a/altosdroid/res/values/CustomTheme.xml b/altosdroid/res/values/CustomTheme.xml index 4daed1f8..6c701ea2 100644 --- a/altosdroid/res/values/CustomTheme.xml +++ b/altosdroid/res/values/CustomTheme.xml @@ -1,7 +1,6 @@ - diff --git a/altosdroid/res/values/strings.xml b/altosdroid/res/values/strings.xml index 1af0dbb7..79a77ba9 100644 --- a/altosdroid/res/values/strings.xml +++ b/altosdroid/res/values/strings.xml @@ -33,6 +33,8 @@ Select data rate Change units Load Maps + Select Tracker + Delete Track Map Type diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index 5be9ba84..942ebbd5 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -42,19 +42,13 @@ import android.content.res.Resources; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.util.DisplayMetrics; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.Window; -import android.view.View; -import android.view.LayoutInflater; -import android.widget.TabHost; -import android.widget.TextView; -import android.widget.RelativeLayout; -import android.widget.Toast; +import android.view.*; +import android.widget.*; import android.app.AlertDialog; import android.location.Location; import android.hardware.usb.*; +import android.graphics.*; +import android.graphics.drawable.*; import org.altusmetrum.altoslib_7.*; @@ -82,9 +76,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { private BluetoothAdapter mBluetoothAdapter = null; - // Layout Views - private TextView mTitle; - // Flight state values private TextView mCallsignView; private TextView mRSSIView; @@ -93,6 +84,9 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { private RelativeLayout mStateLayout; private TextView mStateView; private TextView mAgeView; + private boolean mAgeViewOld; + private int mAgeNewColor; + private int mAgeOldColor; // field to display the version at the bottom of the screen private TextView mVersion; @@ -110,6 +104,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { // Timer and Saved flight state for Age calculation private Timer timer; AltosState saved_state; + TelemetryState telemetry_state; + Integer[] serials; UsbDevice pending_usb_device; boolean start_with_usb; @@ -134,13 +130,11 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { switch (msg.what) { case MSG_STATE: AltosDebug.debug("MSG_STATE"); - TelemetryState telemetry_state = (TelemetryState) msg.obj; - if (telemetry_state == null) { + if (msg.obj == null) { AltosDebug.debug("telemetry_state null!"); return; } - - ad.update_state(telemetry_state); + ad.update_state((TelemetryState) msg.obj); break; case MSG_UPDATE_AGE: AltosDebug.debug("MSG_UPDATE_AGE"); @@ -221,20 +215,20 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400) str = str.concat(String.format(" %d bps", AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate])); - mTitle.setText(str); + setTitle(str); } else { - mTitle.setText(R.string.title_connected_to); + setTitle(R.string.title_connected_to); } break; case TelemetryState.CONNECT_CONNECTING: if (telemetry_state.address != null) - mTitle.setText(String.format("Connecting to %s...", telemetry_state.address.name)); + setTitle(String.format("Connecting to %s...", telemetry_state.address.name)); else - mTitle.setText("Connecting to something..."); + setTitle("Connecting to something..."); break; case TelemetryState.CONNECT_DISCONNECTED: case TelemetryState.CONNECT_NONE: - mTitle.setText(R.string.title_not_connected); + setTitle(R.string.title_not_connected); break; } } @@ -256,17 +250,34 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { boolean registered_units_listener; - void update_state(TelemetryState telemetry_state) { + int current_serial; + + void update_state(TelemetryState new_telemetry_state) { + + if (new_telemetry_state != null) + telemetry_state = new_telemetry_state; + + if (current_serial == 0) + current_serial = telemetry_state.latest_serial; if (!registered_units_listener) { registered_units_listener = true; AltosPreferences.register_units_listener(this); } + serials = telemetry_state.states.keySet().toArray(new Integer[0]); + update_title(telemetry_state); - update_ui(telemetry_state.state, telemetry_state.location); - if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) - start_timer(); + + AltosDebug.debug("update state current serial %d\n", current_serial); + + AltosState state = null; + if (telemetry_state.states.containsKey(current_serial)) + state = telemetry_state.states.get(current_serial); + + update_ui(telemetry_state, state, telemetry_state.location); + + start_timer(); } boolean same_string(String a, String b) { @@ -281,12 +292,55 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } + + private int blend_component(int a, int b, double r, int shift, int mask) { + return ((int) (((a >> shift) & mask) * r + ((b >> shift) & mask) * (1 - r)) & mask) << shift; + } + private int blend_color(int a, int b, double r) { + return (blend_component(a, b, r, 0, 0xff) | + blend_component(a, b, r, 8, 0xff) | + blend_component(a, b, r, 16, 0xff) | + blend_component(a, b, r, 24, 0xff)); + } + + int state_age(AltosState state) { + return (int) ((System.currentTimeMillis() - state.received_time + 500) / 1000); + } + + void set_screen_on(int age) { + if (age < 60) + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + else + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + void update_age() { - if (saved_state != null) - mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000)); + if (saved_state != null) { + int age = state_age(saved_state); + + double age_scale = age / 100.0; + + if (age_scale > 1.0) + age_scale = 1.0; + + mAgeView.setTextColor(blend_color(mAgeOldColor, mAgeNewColor, age_scale)); + + set_screen_on(age); + + String text; + if (age < 60) + text = String.format("%ds", age); + else if (age < 60 * 60) + text = String.format("%dm", age / 60); + else if (age < 60 * 60 * 24) + text = String.format("%dh", age / (60 * 60)); + else + text = String.format("%dd", age / (24 * 60 * 60)); + mAgeView.setText(text); + } } - void update_ui(AltosState state, Location location) { + void update_ui(TelemetryState telem_state, AltosState state, Location location) { int prev_state = AltosLib.ao_flight_invalid; @@ -296,6 +350,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { prev_state = saved_state.state; if (state != null) { + set_screen_on(state_age(state)); + if (state.state == AltosLib.ao_flight_stateless) { boolean prev_locked = false; boolean locked = false; @@ -370,7 +426,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } for (AltosDroidTab mTab : mTabs) - mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem()); + mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem()); if (state != null && mAltosVoice != null) mAltosVoice.tell(state, from_receiver); @@ -427,9 +483,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { fm = getSupportFragmentManager(); // Set up the window layout - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.altosdroid); - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title); // Create the Tabs and ViewPager mTabHost = (TabHost)findViewById(android.R.id.tabhost); @@ -447,11 +501,6 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator(create_tab_view("Map")), TabMap.class, null); mTabsAdapter.addTab(mTabHost.newTabSpec("offmap").setIndicator(create_tab_view("OffMap")), TabMapOffline.class, null); - // Set up the custom title - mTitle = (TextView) findViewById(R.id.title_left_text); - mTitle.setText(R.string.app_name); - mTitle = (TextView) findViewById(R.id.title_right_text); - // Display the Version mVersion = (TextView) findViewById(R.id.version); mVersion.setText("Version: " + BuildInfo.version + @@ -465,6 +514,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { mStateLayout = (RelativeLayout) findViewById(R.id.state_container); mStateView = (TextView) findViewById(R.id.state_value); mAgeView = (TextView) findViewById(R.id.age_value); + mAgeNewColor = mAgeView.getTextColors().getDefaultColor(); + mAgeOldColor = getResources().getColor(R.color.old_color); } private boolean ensureBluetooth() { @@ -737,6 +788,26 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } + void select_tracker(int serial) { + int i; + for (i = 0; i < serials.length; i++) + if (serials[i] == serial) + break; + if (i == serials.length) + return; + + AltosDebug.debug("Switching to serial %d\n", serial); + current_serial = serial; + update_state(null); + } + + void delete_track(int serial) { + try { + mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial)); + } catch (Exception ex) { + } + } + @Override public boolean onOptionsItemSelected(MenuItem item) { Intent serverIntent = null; @@ -817,8 +888,43 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { serverIntent = new Intent(this, MapTypeActivity.class); startActivityForResult(serverIntent, REQUEST_MAP_TYPE); return true; + case R.id.select_tracker: + if (serials != null) { + String[] trackers = new String[serials.length]; + for (int i = 0; i < serials.length; i++) + trackers[i] = String.format("%d", serials[i]); + AlertDialog.Builder builder_serial = new AlertDialog.Builder(this); + builder_serial.setTitle("Select a tracker"); + builder_serial.setItems(trackers, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + select_tracker(serials[item]); + } + }); + AlertDialog alert_serial = builder_serial.create(); + alert_serial.show(); + + } + return true; + case R.id.delete_track: + if (serials != null) { + String[] trackers = new String[serials.length]; + for (int i = 0; i < serials.length; i++) + trackers[i] = String.format("%d", serials[i]); + AlertDialog.Builder builder_serial = new AlertDialog.Builder(this); + builder_serial.setTitle("Delete a track"); + builder_serial.setItems(trackers, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + delete_track(serials[item]); + } + }); + AlertDialog alert_serial = builder_serial.create(); + alert_serial.show(); + + } + return true; } return false; } - } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java index 75676e28..dfc37153 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java @@ -44,7 +44,12 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend { public String[] keys() { Map all = prefs.getAll(); - return (String[])all.keySet().toArray(); + Object[] ao = all.keySet().toArray(); + + String[] as = new String[ao.length]; + for (int i = 0; i < ao.length; i++) + as[i] = (String) ao[i]; + return as; } public AltosPreferencesBackend node(String key) { @@ -104,6 +109,7 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend { } public void remove(String key) { + AltosDebug.debug("remove preference %s\n", key); editor.remove(key); } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java index f1f1b6de..017315af 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -29,11 +29,13 @@ import android.location.Location; import android.widget.TextView; public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener { + TelemetryState last_telem_state; AltosState last_state; AltosGreatCircle last_from_receiver; Location last_receiver; + AltosDroid altos_droid; - public abstract void show(AltosState state, AltosGreatCircle from_receiver, Location receiver); + public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver); public abstract String tab_name(); @@ -41,8 +43,8 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen } public void units_changed(boolean imperial_units) { - if (!isHidden() && last_state != null) - show(last_state, last_from_receiver, last_receiver); + if (!isHidden()) + show(last_telem_state, last_state, last_from_receiver, last_receiver); } public void set_value(TextView text_view, @@ -59,17 +61,27 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen FragmentTransaction ft = AltosDroid.fm.beginTransaction(); AltosDebug.debug("set visible %b %s\n", visible, tab_name()); if (visible) { - AltosState state = last_state; - AltosGreatCircle from_receiver = last_from_receiver; - Location receiver = last_receiver; - ft.show(this); - show(state, from_receiver, receiver); + show(last_telem_state, last_state, last_from_receiver, last_receiver); } else ft.hide(this); ft.commitAllowingStateLoss(); } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + altos_droid = (AltosDroid) activity; + altos_droid.registerTab(this); + } + + @Override + public void onDetach() { + super.onDetach(); + altos_droid.unregisterTab(this); + altos_droid = null; + } + @Override public void onResume() { super.onResume(); @@ -77,12 +89,16 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen set_visible(true); } - public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) { + public void update_ui(TelemetryState telem_state, AltosState state, + AltosGreatCircle from_receiver, Location receiver, boolean is_current) + { + last_telem_state = telem_state; last_state = state; last_from_receiver = from_receiver; last_receiver = receiver; + AltosDebug.debug("update_ui tab %s is_current %b\n", tab_name(), is_current); if (is_current) - show(state, from_receiver, receiver); + show(telem_state, state, from_receiver, receiver); else return; } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java index 4af117a2..f36ef267 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java @@ -191,14 +191,22 @@ public class DeviceListActivity extends Activity { // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { - // Get the BluetoothDevice object from the Intent + + /* Get the BluetoothDevice object from the Intent + */ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - // If it's already paired, skip it, because it's been listed already - if ( device.getBondState() != BluetoothDevice.BOND_BONDED - && device.getName().startsWith("TeleBT") ) { - mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); + + /* If it's already paired, skip it, because it's been listed already + */ + if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED) + { + String name = device.getName(); + if (name != null && name.startsWith("TeleBT")) + mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } - // When discovery is finished, change the Activity title + + /* When discovery is finished, change the Activity title + */ } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setProgressBarIndeterminateVisibility(false); setTitle(R.string.select_device); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java index 797dc7c3..afce937f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java @@ -30,8 +30,6 @@ import android.widget.TextView; import android.location.Location; public class TabAscent extends AltosDroidTab { - AltosDroid mAltosDroid; - private TextView mHeightView; private TextView mMaxHeightView; private TextView mSpeedView; @@ -45,13 +43,6 @@ public class TabAscent extends AltosDroidTab { private TextView mMainVoltageView; private GoNoGoLights mMainLights; - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.tab_ascent, container, false); @@ -78,18 +69,11 @@ public class TabAscent extends AltosDroidTab { return v; } - @Override - public void onDestroy() { - super.onDestroy(); - mAltosDroid.unregisterTab(this); - mAltosDroid = null; - } - public String tab_name() { return "ascent"; } - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (state != null) { set_value(mHeightView, AltosConvert.height, 6, state.height()); set_value(mHeightView, AltosConvert.height, 6, state.height()); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java index f3f5a0f1..3429ee72 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java @@ -30,8 +30,6 @@ import android.widget.TextView; import android.location.Location; public class TabDescent extends AltosDroidTab { - AltosDroid mAltosDroid; - private TextView mSpeedView; private TextView mHeightView; private TextView mElevationView; @@ -46,14 +44,6 @@ public class TabDescent extends AltosDroidTab { private TextView mMainVoltageView; private GoNoGoLights mMainLights; - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.tab_descent, container, false); @@ -81,17 +71,9 @@ public class TabDescent extends AltosDroidTab { return v; } - - @Override - public void onDestroy() { - super.onDestroy(); - mAltosDroid.unregisterTab(this); - mAltosDroid = null; - } - public String tab_name() { return "descent"; } - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (state != null) { set_value(mSpeedView, AltosConvert.speed, 6, state.speed()); set_value(mHeightView, AltosConvert.height, 6, state.height()); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java index d37891f7..dd3f938e 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java @@ -29,8 +29,6 @@ import android.widget.TextView; import android.location.Location; public class TabLanded extends AltosDroidTab { - AltosDroid mAltosDroid; - private TextView mBearingView; private TextView mDistanceView; private TextView mTargetLatitudeView; @@ -41,14 +39,6 @@ public class TabLanded extends AltosDroidTab { private TextView mMaxSpeedView; private TextView mMaxAccelView; - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.tab_landed, container, false); @@ -66,16 +56,9 @@ public class TabLanded extends AltosDroidTab { return v; } - @Override - public void onDestroy() { - super.onDestroy(); - mAltosDroid.unregisterTab(this); - mAltosDroid = null; - } - public String tab_name() { return "landed"; } - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (from_receiver != null) { mBearingView.setText(String.format("%3.0f°", from_receiver.bearing)); set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index cea3cac6..ff36875d 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -33,6 +33,7 @@ import com.google.android.gms.maps.model.PolylineOptions; import android.app.Activity; import android.graphics.Color; +import android.graphics.*; import android.os.Bundle; import android.support.v4.app.Fragment; //import android.support.v4.app.FragmentTransaction; @@ -43,8 +44,6 @@ import android.widget.TextView; import android.location.Location; public class TabMap extends AltosDroidTab { - AltosDroid mAltosDroid; - private SupportMapFragment mMapFragment; private GoogleMap mMap; private boolean mapLoaded = false; @@ -63,11 +62,40 @@ public class TabMap extends AltosDroidTab { private double mapAccuracy = -1; - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); + private Bitmap rocket_bitmap(String text) { + + /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/ + */ + Bitmap orig_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket); + Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true); + + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(); + paint.setTextSize(40); + paint.setColor(0xff000000); + + Rect bounds = new Rect(); + paint.getTextBounds(text, 0, text.length(), bounds); + + int width = bounds.right - bounds.left; + int height = bounds.bottom - bounds.top; + + float x = bitmap.getWidth() / 2.0f - width / 2.0f; + float y = bitmap.getHeight() / 2.0f - height / 2.0f; + + AltosDebug.debug("map label x %f y %f\n", x, y); + + canvas.drawText(text, 0, text.length(), x, y, paint); + return bitmap; + } + + private Marker rocket_marker(int serial, double lat, double lon) { + Bitmap bitmap = rocket_bitmap(String.format("%d", serial)); + + return mMap.addMarker(new MarkerOptions() + .icon(BitmapDescriptorFactory.fromBitmap(bitmap)) + .position(new LatLng(lat, lon)) + .visible(false)); } @Override @@ -102,33 +130,17 @@ public class TabMap extends AltosDroidTab { getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit(); } - @Override - public void onDestroyView() { - super.onDestroyView(); - - mAltosDroid.unregisterTab(this); - mAltosDroid = null; - - //Fragment fragment = (getFragmentManager().findFragmentById(R.id.map)); - //FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction(); - //ft.remove(fragment); - //ft.commit(); - } - private void setupMap() { mMap = mMapFragment.getMap(); if (mMap != null) { - set_map_type(mAltosDroid.map_type); + set_map_type(altos_droid.map_type); mMap.setMyLocationEnabled(true); mMap.getUiSettings().setTiltGesturesEnabled(false); mMap.getUiSettings().setZoomControlsEnabled(false); - mRocketMarker = mMap.addMarker( - // From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/ - new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.rocket)) - .position(new LatLng(0,0)) - .visible(false) - ); + Bitmap label_bitmap = rocket_bitmap("hello"); + + mRocketMarker = rocket_marker(1800,0,0); mPadMarker = mMap.addMarker( new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad)) @@ -156,7 +168,7 @@ public class TabMap extends AltosDroidTab { public String tab_name() { return "map"; } - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (from_receiver != null) { mBearingView.setText(String.format("%3.0f°", from_receiver.bearing)); set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance); @@ -166,6 +178,8 @@ public class TabMap extends AltosDroidTab { if (mapLoaded) { if (state.gps != null) { mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon)); + mRocketMarker.setTitle("hello world"); + mRocketMarker.setSnippet("hello"); mRocketMarker.setVisible(true); mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon))); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java index 56e296d9..4b728c23 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMapOffline.java @@ -54,8 +54,6 @@ class Rocket { public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { - AltosDroid mAltosDroid; - AltosMap map; AltosLatLon here; @@ -325,11 +323,9 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { @Override public void onAttach(Activity activity) { super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); map = new AltosMap(this); - map.set_maptype(mAltosDroid.map_type); + map.set_maptype(altos_droid.map_type); pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad); /* arrow at the bottom of the launchpad image */ @@ -347,12 +343,6 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { here_off_y = here_bitmap.getHeight() / 2; } - @Override - public void onDetach() { - super.onDetach(); - mAltosDroid = null; - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -382,7 +372,6 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { public void onDestroyView() { super.onDestroyView(); - mAltosDroid.unregisterTab(this); } private void center(double lat, double lon, double accuracy) { @@ -395,7 +384,7 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { public String tab_name() { return "offmap"; } - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (from_receiver != null) { mBearingView.setText(String.format("%3.0f°", from_receiver.bearing)); set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance); @@ -411,20 +400,30 @@ public class TabMapOffline extends AltosDroidTab implements AltosMapInterface { } if (state.pad_lat != AltosLib.MISSING && pad == null) pad = new AltosLatLon(state.pad_lat, state.pad_lon); + } - int serial = state.serial; - if (serial == AltosLib.MISSING) - serial = 0; + if (telem_state != null) { + Integer[] old_serial = rockets.keySet().toArray(new Integer[0]); + Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]); - Rocket rocket = null; + /* remove deleted keys */ + for (int serial : old_serial) { + if (!telem_state.states.containsKey(serial)) + rockets.remove(serial); + } + + /* set remaining keys */ - if (state.gps != null && state.gps.locked) { - if (!rockets.containsKey(serial)) { + for (int serial : new_serial) { + Rocket rocket; + AltosState t_state = telem_state.states.get(serial); + if (rockets.containsKey(serial)) + rocket = rockets.get(serial); + else { rocket = new Rocket(String.format("%d", serial), this); rockets.put(serial, rocket); - } else - rocket = rockets.get(serial); - rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon)); + } + rocket.set_position(new AltosLatLon(t_state.gps.lat, t_state.gps.lon)); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java index f7eb43db..7a256963 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java @@ -30,8 +30,6 @@ import android.widget.TextView; import android.location.Location; public class TabPad extends AltosDroidTab { - AltosDroid mAltosDroid; - private TextView mBatteryVoltageView; private TextView mBatteryVoltageLabel; private GoNoGoLights mBatteryLights; @@ -51,13 +49,6 @@ public class TabPad extends AltosDroidTab { private TextView mPadLongitudeView; private TextView mPadAltitudeView; - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.tab_pad, container, false); @@ -100,16 +91,9 @@ public class TabPad extends AltosDroidTab { return v; } - @Override - public void onDestroy() { - super.onDestroy(); - mAltosDroid.unregisterTab(this); - mAltosDroid = null; - } - public String tab_name() { return "pad"; } - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (state != null) { mBatteryVoltageView.setText(AltosDroid.number("%4.2f V", state.battery_voltage)); mBatteryLights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java index 7c3c2268..28bab401 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java @@ -34,7 +34,7 @@ public class TelemetryLogger { logger = null; } } - + void handleExternalStorageState() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java index 7b29fe44..4d4f0ed1 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java @@ -21,6 +21,7 @@ package org.altusmetrum.AltosDroid; import java.text.*; import java.io.*; +import java.util.*; import java.util.concurrent.*; import android.os.Handler; @@ -34,25 +35,18 @@ public class TelemetryReader extends Thread { Handler handler; AltosLink link; - AltosState state = null; LinkedBlockingQueue telemQueue; - public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException { + public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException { AltosLine l = telemQueue.take(); if (l.line == null) throw new IOException("IO error"); AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line); - if (state == null) - state = new AltosState(); - else - state = state.clone(); - telem.update_state(state); - return state; + return telem; } public void close() { - state = null; link.remove_monitor(telemQueue); link = null; telemQueue.clear(); @@ -60,14 +54,12 @@ public class TelemetryReader extends Thread { } public void run() { - AltosState state = null; - try { AltosDebug.debug("starting loop"); while (telemQueue != null) { try { - state = read(); - handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget(); + AltosTelemetry telem = read(); + handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget(); } catch (ParseException pp) { AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage()); } catch (AltosCRCException ce) { @@ -84,12 +76,11 @@ public class TelemetryReader extends Thread { } } - public TelemetryReader (AltosLink in_link, Handler in_handler, AltosState in_state) { + public TelemetryReader (AltosLink in_link, Handler in_handler) { AltosDebug.debug("connected TelemetryReader create started"); link = in_link; handler = in_handler; - state = in_state; telemQueue = new LinkedBlockingQueue(); link.add_monitor(telemQueue); link.set_telemetry(AltosLib.ao_telemetry_standard); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index eae360db..80694ea7 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -18,10 +18,8 @@ package org.altusmetrum.AltosDroid; import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.concurrent.TimeoutException; -import java.util.Timer; -import java.util.TimerTask; +import java.util.*; import android.app.Notification; //import android.app.NotificationManager; @@ -62,6 +60,7 @@ public class TelemetryService extends Service implements LocationListener { static final int MSG_CRC_ERROR = 10; static final int MSG_SETBAUD = 11; static final int MSG_DISCONNECT = 12; + static final int MSG_DELETE_SERIAL = 13; // Unique Identification Number for the Notification. // We use it on Notification start, and to cancel it. @@ -120,6 +119,10 @@ public class TelemetryService extends Service implements LocationListener { s.address = null; s.disconnect(true); break; + case MSG_DELETE_SERIAL: + AltosDebug.debug("Delete Serial command received"); + s.delete_serial((Integer) msg.obj); + break; case MSG_SETFREQUENCY: AltosDebug.debug("MSG_SETFREQUENCY"); s.telemetry_state.frequency = (Double) msg.obj; @@ -197,13 +200,8 @@ public class TelemetryService extends Service implements LocationListener { * Messages from TelemetryReader */ case MSG_TELEMETRY: - s.telemetry_state.state = (AltosState) msg.obj; - if (s.telemetry_state.state != null) { - AltosDebug.debug("Save state"); - AltosPreferences.set_state(0, s.telemetry_state.state, null); - } AltosDebug.debug("MSG_TELEMETRY"); - s.send_to_clients(); + s.telemetry((AltosTelemetry) msg.obj); break; case MSG_CRC_ERROR: // forward crc error messages @@ -217,13 +215,31 @@ public class TelemetryService extends Service implements LocationListener { } } + /* Handle telemetry packet + */ + private void telemetry(AltosTelemetry telem) { + AltosState state; + + if (telemetry_state.states.containsKey(telem.serial)) + state = telemetry_state.states.get(telem.serial).clone(); + else + state = new AltosState(); + telem.update_state(state); + telemetry_state.states.put(telem.serial, state); + if (state != null) { + AltosDebug.debug("Save state %d", telem.serial); + AltosPreferences.set_state(telem.serial, state, null); + } + send_to_clients(); + } + /* Construct the message to deliver to clients */ private Message message() { if (telemetry_state == null) AltosDebug.debug("telemetry_state null!"); - if (telemetry_state.state == null) - AltosDebug.debug("telemetry_state.state null!"); + if (telemetry_state.states == null) + AltosDebug.debug("telemetry_state.states null!"); return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state); } @@ -332,6 +348,12 @@ public class TelemetryService extends Service implements LocationListener { } } + private void delete_serial(int serial) { + telemetry_state.states.remove((Integer) serial); + AltosPreferences.remove_state(serial); + send_to_clients(); + } + private void start_altos_bluetooth(DeviceAddress address, boolean pause) { // Get the BLuetoothDevice object BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address); @@ -372,7 +394,7 @@ public class TelemetryService extends Service implements LocationListener { telemetry_state.connect = TelemetryState.CONNECT_CONNECTED; telemetry_state.address = address; - telemetry_reader = new TelemetryReader(altos_link, handler, telemetry_state.state); + telemetry_reader = new TelemetryReader(altos_link, handler); telemetry_reader.start(); AltosDebug.debug("connected TelemetryReader started"); @@ -406,11 +428,32 @@ public class TelemetryService extends Service implements LocationListener { telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED; telemetry_state.address = null; - AltosSavedState saved_state = AltosPreferences.state(0); + /* Pull the saved state information out of the preferences database + */ + ArrayList serials = AltosPreferences.list_states(); - if (saved_state != null) { - AltosDebug.debug("recovered old state flight %d\n", saved_state.state.flight); - telemetry_state.state = saved_state.state; + telemetry_state.latest_serial = AltosPreferences.latest_state(); + + for (int serial : serials) { + AltosSavedState saved_state = AltosPreferences.state(serial); + if (saved_state != null) { + if (serial == 0) { + serial = saved_state.state.serial; + AltosPreferences.set_state(serial, saved_state.state, saved_state.listener_state); + AltosPreferences.remove_state(0); + } + if (telemetry_state.latest_serial == 0) + telemetry_state.latest_serial = serial; + + AltosDebug.debug("recovered old state serial %d flight %d\n", + serial, + saved_state.state.flight); + if (saved_state.state.gps != null) + AltosDebug.debug("\tposition %f,%f\n", + saved_state.state.gps.lat, + saved_state.state.gps.lon); + telemetry_state.states.put(serial, saved_state.state); + } } // Listen for GPS and Network position updates diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java index fb7e1893..d023128f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java @@ -17,6 +17,7 @@ package org.altusmetrum.AltosDroid; +import java.util.*; import org.altusmetrum.altoslib_7.*; import android.location.Location; @@ -29,16 +30,19 @@ public class TelemetryState { int connect; DeviceAddress address; AltosConfigData config; - AltosState state; Location location; int crc_errors; double frequency; int telemetry_rate; + HashMap states; + + int latest_serial; + public TelemetryState() { connect = CONNECT_NONE; config = null; - state = null; + states = new HashMap(); location = null; crc_errors = 0; frequency = AltosPreferences.frequency(0); diff --git a/altoslib/AltosPreferences.java b/altoslib/AltosPreferences.java index 2fad96fd..cdff93f6 100644 --- a/altoslib/AltosPreferences.java +++ b/altoslib/AltosPreferences.java @@ -19,6 +19,7 @@ package org.altusmetrum.altoslib_7; import java.io.*; import java.util.*; +import java.text.*; public class AltosPreferences { public static AltosPreferencesBackend backend = null; @@ -42,7 +43,9 @@ public class AltosPreferences { public final static String logfilePreferenceFormat = "LOGFILE-%d"; /* state preference name */ + public final static String statePreferenceHead = "STATE-"; public final static String statePreferenceFormat = "STATE-%d"; + public final static String statePreferenceLatest = "STATE-LATEST"; /* voice preference name */ public final static String voicePreference = "VOICE"; @@ -360,12 +363,43 @@ public class AltosPreferences { synchronized(backend) { backend.putBytes(String.format(statePreferenceFormat, serial), bytes); + backend.putInt(statePreferenceLatest, serial); flush_preferences(); } } catch (IOException ie) { } } + public static ArrayList list_states() { + String[] keys = backend.keys(); + ArrayList states = new ArrayList(); + + for (String key : keys) { + if (key.startsWith(statePreferenceHead)) { + try { + int serial = AltosParse.parse_int(key.substring(statePreferenceHead.length())); + states.add(serial); + } catch (ParseException pe) { + } + } + } + return states; + } + + public static void remove_state(int serial) { + synchronized(backend) { + backend.remove(String.format(statePreferenceFormat, serial)); + } + } + + public static int latest_state() { + int latest = 0; + synchronized (backend) { + latest = backend.getInt(statePreferenceLatest, 0); + } + return latest; + } + public static AltosSavedState state(int serial) { byte[] bytes = null; diff --git a/ao-bringup/cal-freq b/ao-bringup/cal-freq index 5d876e21..d3d9dc95 100755 --- a/ao-bringup/cal-freq +++ b/ao-bringup/cal-freq @@ -10,42 +10,16 @@ case $# in ;; esac -while true; do - echo 'C 1' > $dev - - echo -n "Generating RF carrier. Please enter measured frequency [enter for done]: " - - read FREQ - - echo 'C 0' > $dev - +../ao-tools/ao-cal-freq/ao-cal-freq --dev=$dev +case $? in + 0) calline=`./get-radio-cal $dev` - CURRENT_CAL=`echo $calline | awk '{print $2}'` + CAL_VALUE=`echo $calline | awk '{print $2}'` CURRENT_FREQ=`echo $calline | awk '{print $4}'` - CAL_VALUE=$CURRENT_CAL - - case "$FREQ" in - "") - echo $SERIAL","$CAL_VALUE >> cal_values - exit 0 - ;; - *) - echo "Current radio calibration "$CURRENT_CAL - echo "Current radio frequency "$CURRENT_FREQ - - CAL_VALUE=`nickle -e "floor($CURRENT_FREQ / $FREQ * $CURRENT_CAL + 0.5)"` - - echo "Programming flash with cal value " $CAL_VALUE - - dd if=$dev iflag=nonblock - - cat << EOF > $dev -c f $CAL_VALUE -c w -EOF - - echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE - ;; - esac -done - + echo $SERIAL","$CAL_VALUE >> cal_values + exit 0 + ;; + *) + exit 1 + ;; +esac diff --git a/ao-bringup/turnon_telegps b/ao-bringup/turnon_telegps index 123f0b54..ba97d503 100755 --- a/ao-bringup/turnon_telegps +++ b/ao-bringup/turnon_telegps @@ -72,10 +72,8 @@ case "$dev" in ;; esac -echo 'E 0' > $dev +SERIAL=$SERIAL ./cal-freq $dev ./test-telegps -SERIAL=$SERIAL ./cal-freq $dev - exit $? diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index 6a170cbd..66d2560e 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -2,7 +2,8 @@ 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-cal-accel ao-test-gps ao-usbtrng + ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng \ + ao-cal-freq if LIBSTLINK SUBDIRS += ao-stmload endif diff --git a/ao-tools/ao-cal-freq/.gitignore b/ao-tools/ao-cal-freq/.gitignore new file mode 100644 index 00000000..4fe14865 --- /dev/null +++ b/ao-tools/ao-cal-freq/.gitignore @@ -0,0 +1 @@ +ao-cal-freq diff --git a/ao-tools/ao-cal-freq/Makefile.am b/ao-tools/ao-cal-freq/Makefile.am new file mode 100644 index 00000000..e11c2b0a --- /dev/null +++ b/ao-tools/ao-cal-freq/Makefile.am @@ -0,0 +1,11 @@ +bin_PROGRAMS=ao-cal-freq + +AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) + +ao_cal_freq_DEPENDENCIES = $(top_builddir)/ao-tools/lib/libao-tools.a + +ao_cal_freq_LDADD=$(top_builddir)/ao-tools/lib/libao-tools.a $(LIBUSB_LIBS) -lm + +ao_cal_freq_SOURCES=ao-cal-freq.c + +man_MANS = ao-cal-freq.1 diff --git a/ao-tools/ao-cal-freq/ao-cal-freq.1 b/ao-tools/ao-cal-freq/ao-cal-freq.1 new file mode 100644 index 00000000..bfc3101c --- /dev/null +++ b/ao-tools/ao-cal-freq/ao-cal-freq.1 @@ -0,0 +1,58 @@ +.\" +.\" Copyright © 2009 Keith Packard +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License along +.\" with this program; if not, write to the Free Software Foundation, Inc., +.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +.\" +.\" +.TH AO-LOAD 1 "ao-cal-freq" "" +.SH NAME +ao-cal-freq \- Calibrate AltOS flight computer frequency +.SH SYNOPSIS +.B "ao-cal-freq" +[\-T \fItty-device\fP] +[\--tty \fItty-device\fP] +[\-D \fIaltos-device\fP] +[\--device \fIaltos-device\fP] +.SH DESCRIPTION +.I ao-cal-freq +drives the frequency calibration process and saves the result. +.SH OPTIONS +.TP +\-T tty-device | --tty tty-device +This selects which tty device the debugger uses to communicate with +the target device. The special name 'BITBANG' directs ao-dbg to use +the cp2103 connection, otherwise this should be a usb serial port +connected to a suitable cc1111 debug node. +.TP +\-D AltOS-device | --device AltOS-device +Search for a connected device. This requires an argument of one of the +following forms: +.IP +TeleMega:2 +.br +TeleMega +.br +2 +.IP +Leaving out the product name will cause the tool to select a suitable +product, leaving out the serial number will cause the tool to match +one of the available devices. +.SH USAGE +.I ao-cal-freq +opens the target device, interactively calibrates the frequency, then +shows the resulting calibration values and saves them to configuration +memory. +.SH AUTHOR +Keith Packard diff --git a/ao-tools/ao-cal-freq/ao-cal-freq.c b/ao-tools/ao-cal-freq/ao-cal-freq.c new file mode 100644 index 00000000..464faf0f --- /dev/null +++ b/ao-tools/ao-cal-freq/ao-cal-freq.c @@ -0,0 +1,280 @@ +/* + * Copyright © 2014 Keith Packard + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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=] [--device=] [-tty=]\n", program); + exit(1); +} + +void +done(struct cc_usb *cc, int code) +{ + cc_usb_close(cc); + exit (code); +} + +static int +ends_with(char *whole, char *suffix) +{ + int whole_len = strlen(whole); + int suffix_len = strlen(suffix); + + if (suffix_len > whole_len) + return 0; + return strcmp(whole + whole_len - suffix_len, suffix) == 0; +} + +static int +starts_with(char *whole, char *prefix) +{ + int whole_len = strlen(whole); + int prefix_len = strlen(prefix); + + if (prefix_len > whole_len) + return 0; + return strncmp(whole, prefix, prefix_len) == 0; +} + +static char ** +tok(char *line) { + char **strs = malloc (sizeof (char *)), *str; + int n = 0; + + while ((str = strtok(line, " \t"))) { + line = NULL; + strs = realloc(strs, (n + 2) * sizeof (char *)); + strs[n] = strdup(str); + n++; + } + strs[n] = '\0'; + return strs; +} + +static void +free_strs(char **strs) { + char *str; + int i; + + for (i = 0; (str = strs[i]) != NULL; i++) + free(str); + free(strs); +} + +struct flash { + struct flash *next; + char line[512]; + char **strs; +}; + +static struct flash * +flash(struct cc_usb *usb) +{ + struct flash *head = NULL, **tail = &head; + cc_usb_printf(usb, "c s\nv\n"); + for (;;) { + char line[512]; + struct flash *b; + + cc_usb_getline(usb, line, sizeof (line)); + b = malloc (sizeof (struct flash)); + strcpy(b->line, line); + b->strs = tok(line); + b->next = NULL; + *tail = b; + tail = &b->next; + if (strstr(line, "software-version")) + break; + } + return head; +} + +static void +free_flash(struct flash *b) { + struct flash *n; + + while (b) { + n = b->next; + free_strs(b->strs); + free(b); + b = n; + } +} + +char ** +find_flash(struct flash *b, char *word0) { + int i; + for (;b; b = b->next) { + if (strstr(b->line, word0)) + return b->strs; + } + return NULL; +} + +void +await_key(void) +{ + struct termios termios, termios_save; + char buf[512]; + + tcgetattr(0, &termios); + termios_save = termios; + cfmakeraw(&termios); + tcsetattr(0, TCSAFLUSH, &termios); + read(0, buf, sizeof (buf)); + tcsetattr(0, TCSAFLUSH, &termios_save); +} + +int +do_cal(struct cc_usb *usb) { + struct flash *b; + char line[1024]; + double measured_freq; + char **cur_freq_words; + char **cur_cal_words; + char *line_end; + int cur_freq; + int cur_cal; + int new_cal; + + cc_usb_printf(usb, "E 0\n"); + + for(;;) { + cc_usb_printf(usb, "C 1\n"); + cc_usb_sync(usb); + + printf("Generating RF carrier. Please enter measured frequency [enter for done]: "); + fflush(stdout); + fgets(line, sizeof (line) - 1, stdin); + cc_usb_printf(usb, "C 0\n"); + cc_usb_sync(usb); + + measured_freq = strtod(line, &line_end); + if (line_end == line) + break; + + b = flash(usb); + + cur_cal_words = find_flash(b, "Radio cal:"); + cur_freq_words = find_flash(b, "Frequency:"); + + if (!cur_cal_words || !cur_freq_words) { + printf("no response\n"); + return 0; + } + + cur_cal = atoi(cur_cal_words[2]); + cur_freq = atoi(cur_freq_words[1]); + + printf ("Current radio calibration %d\n", cur_cal); + printf ("Current radio frequency: %d\n", cur_freq); + + + new_cal = floor ((((double) cur_freq / 1000.0) / measured_freq) * cur_cal + 0.5); + + printf ("Programming flash with cal value %d\n", new_cal); + + cc_usb_printf (usb, "c f %d\nc w\n", new_cal); + cc_usb_sync(usb); + } + return 1; +} + +int +main (int argc, char **argv) +{ + char *device = NULL; + char *filename; + Elf *e; + unsigned int s; + int i; + int c; + int tries; + struct cc_usb *cc = NULL; + char *tty = NULL; + int success; + int verbose = 0; + int ret = 0; + int expected_size; + + while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) { + switch (c) { + case 'T': + tty = optarg; + break; + case 'D': + device = optarg; + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + break; + } + } + + ao_verbose = verbose; + + if (verbose > 1) + ccdbg_add_debug(CC_DEBUG_BITBANG); + + if (!tty) + tty = cc_usbdevs_find_by_arg(device, "AltosFlash"); + if (!tty) + tty = cc_usbdevs_find_by_arg(device, "TeleMega"); + if (!tty) + tty = getenv("ALTOS_TTY"); + if (!tty) + tty="/dev/ttyACM0"; + + cc = cc_usb_open(tty); + + if (!cc) + exit(1); + + if (!do_cal(cc)) + ret = 1; + done(cc, ret); +} diff --git a/configure.ac b/configure.ac index d7a14aa8..e0d5f422 100644 --- a/configure.ac +++ b/configure.ac @@ -551,6 +551,7 @@ 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-tools/ao-cal-freq/Makefile ao-tools/ao-test-gps/Makefile ao-tools/ao-usbtrng/Makefile ao-utils/Makefile