ao-tools/ao-bitbang/ao-bitbang
ao-tools/ao-dbg/ao-dbg
ao-tools/ao-dumplog/ao-dumplog
+ao-tools/ao-dump-up/ao-dump-up
ao-tools/ao-eeprom/ao-eeprom
ao-tools/ao-edit-telem/ao-edit-telem
ao-tools/ao-list/ao-list
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.altusmetrum.AltosDroid"
- android:versionCode="1"
- android:versionName="1.1.9.3">
+ android:versionCode="3"
+ android:versionName="1.2">
<uses-sdk android:targetSdkVersion="10" android:minSdkVersion="10"/>
<!-- Google Maps -->
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
<!-- Permissions needed for GoogleMaps -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
--- /dev/null
+Desired AltosDroid feature list
+
+ *) GPS satellite status tab. Monitor GPS C/N0 numbers and
+ SVIDs. Provides more info before GPS lock is acquired.
+
+ *) Channel scanning. Provide the ability to search for telemetry
+ signals like AltosUI does.
+
+ *) Highlight current frequency in the frequency list.
+
+ *) 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,
+ or perhaps something better.
+
+ *) TM configuration from the droid
+
+ *) Monitor-idle mode
+
+ *) Frequency scanning
+
+ *) Select satellite imaging mode
android:layout_height="wrap_content"
android:layout_below="@+id/callsign_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/serial_label"
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/flight_label"
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/state_label"
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/rssi_label"
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/age_label"
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_below="@id/height_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/max_height_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_below="@id/speed_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/max_speed_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_below="@id/accel_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/max_accel_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_below="@id/lat_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/lon_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/apogee_voltage_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/apogee_greenled"
android:layout_below="@id/apogee_voltage_label"
android:layout_toRightOf="@id/apogee_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/main_voltage_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/main_greenled"
android:layout_below="@id/main_voltage_label"
android:layout_toRightOf="@id/main_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
android:layout_alignParentRight="true"
android:layout_below="@id/speed_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/height_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_below="@id/elevation_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/range_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_below="@id/bearing_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/compass_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_below="@id/distance_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<TextView
android:layout_alignParentRight="true"
android:layout_below="@id/lat_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/lon_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/apogee_voltage_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/apogee_greenled"
android:layout_below="@id/apogee_voltage_label"
android:layout_toRightOf="@id/apogee_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/main_voltage_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/main_greenled"
android:layout_below="@id/main_voltage_label"
android:layout_toRightOf="@id/main_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:layout_alignParentRight="true"
android:layout_below="@+id/bearing_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@+id/distance_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:paddingTop="5dp" >
<TextView
- android:id="@+id/lat_label"
+ android:id="@+id/target_lat_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/latitude_label" />
+ android:text="@string/target_latitude_label" />
<TextView
- android:id="@+id/lat_value"
+ android:id="@+id/target_lat_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
- android:layout_below="@id/lat_label"
+ android:layout_below="@id/target_lat_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:paddingTop="5dp" >
<TextView
- android:id="@+id/lon_label"
+ android:id="@+id/target_lon_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/longitude_label" />
+ android:text="@string/target_longitude_label" />
<TextView
- android:id="@+id/lon_value"
+ android:id="@+id/target_lon_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
- android:layout_below="@id/lon_label"
+ android:layout_below="@id/target_lon_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/receiver_lat_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/receiver_latitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lat_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/receiver_lat_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/receiver_lon_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/receiver_longitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lon_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/receiver_lon_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/max_height_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/max_speed_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_alignParentRight="true"
android:layout_below="@id/max_accel_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
android:id="@+id/distance_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:paddingRight="4dp"
android:text="@string/distance_label" />
<TextView
android:id="@+id/distance_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/distance_label"
+ android:layout_toRightOf="@+id/distance_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/bearing_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:paddingRight="4dp"
android:text="@string/bearing_label" />
<TextView
android:id="@+id/bearing_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/bearing_label"
+ android:layout_toRightOf="@+id/bearing_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
android:paddingTop="5dp" >
<TextView
- android:id="@+id/lat_label"
+ android:id="@+id/target_lat_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/latitude_label" />
+ android:paddingRight="4dp"
+ android:text="@string/target_latitude_label" />
<TextView
- android:id="@+id/lat_value"
+ android:id="@+id/target_lat_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/lat_label"
+ android:layout_toRightOf="@id/target_lat_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:paddingTop="5dp" >
<TextView
- android:id="@+id/lon_label"
+ android:id="@+id/target_lon_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/longitude_label" />
+ android:paddingRight="4dp"
+ android:text="@string/target_longitude_label" />
<TextView
- android:id="@+id/lon_value"
+ android:id="@+id/target_lon_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/lon_label"
+ android:layout_toRightOf="@id/target_lon_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="true"
+ android:orientation="horizontal" >
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/receiver_lat_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/receiver_latitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lat_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/receiver_lat_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="5dp" >
+
+ <TextView
+ android:id="@+id/receiver_lon_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="4dp"
+ android:text="@string/receiver_longitude_label" />
+
+ <TextView
+ android:id="@+id/receiver_lon_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/receiver_lon_label"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/battery_voltage_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/battery_greenled"
android:layout_below="@id/battery_voltage_label"
android:layout_toRightOf="@id/battery_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/apogee_voltage_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/apogee_greenled"
android:layout_below="@id/apogee_voltage_label"
android:layout_toRightOf="@id/apogee_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/main_voltage_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/main_greenled"
android:layout_below="@id/main_voltage_label"
android:layout_toRightOf="@id/main_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/logging_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/logging_greenled"
android:layout_below="@id/logging_label"
android:layout_toRightOf="@id/logging_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/gps_locked_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/gps_locked_greenled"
android:layout_below="@id/gps_locked_label"
android:layout_toRightOf="@id/gps_locked_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/gps_ready_label"
- android:src="@drawable/redled" />
+ android:src="@drawable/grayled" />
<ImageView
android:id="@+id/gps_ready_greenled"
android:layout_below="@id/gps_ready_label"
android:layout_toRightOf="@id/gps_ready_greenled"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="69dp"
- android:paddingTop="5dp" >
+ android:paddingLeft="69dp">
<TextView
android:id="@+id/pad_lat_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:width="100sp"
+ android:paddingRight="10sp"
android:layout_toRightOf="@id/gps_ready_greenled"
android:text="@string/pad_lat_label" />
android:id="@+id/pad_lat_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_below="@id/pad_lat_label"
- android:layout_toRightOf="@id/gps_ready_greenled"
+ android:layout_toRightOf="@id/pad_lat_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="69dp"
- android:paddingTop="5dp" >
+ android:paddingLeft="69dp">
<TextView
android:id="@+id/pad_lon_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:width="100sp"
+ android:paddingRight="10sp"
android:layout_toRightOf="@id/gps_ready_greenled"
android:text="@string/pad_lon_label" />
android:id="@+id/pad_lon_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_below="@id/pad_lon_label"
- android:layout_toRightOf="@id/gps_ready_greenled"
+ android:layout_toRightOf="@id/pad_lon_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="69dp"
- android:paddingTop="5dp" >
+ android:paddingLeft="69dp">
<TextView
android:id="@+id/pad_alt_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:width="100sp"
+ android:paddingRight="10sp"
android:layout_toRightOf="@id/gps_ready_greenled"
android:text="@string/pad_alt_label" />
android:id="@+id/pad_alt_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_below="@id/pad_alt_label"
- android:layout_toRightOf="@id/gps_ready_greenled"
+ android:layout_toRightOf="@id/pad_alt_label"
android:text=""
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
<string name="gps_ready_label">GPS Ready</string>
<string name="latitude_label">Latitude</string>
<string name="longitude_label">Longitude</string>
- <string name="pad_lat_label">Pad Latitude</string>
- <string name="pad_lon_label">Pad Longitude</string>
- <string name="pad_alt_label">Pad Altitude</string>
+ <string name="target_latitude_label">Tar Lat</string>
+ <string name="target_longitude_label">Tar Lon</string>
+ <string name="receiver_latitude_label">My Lat</string>
+ <string name="receiver_longitude_label">My Lon</string>
+ <string name="pad_lat_label">Pad Lat</string>
+ <string name="pad_lon_label">Pad Lon</string>
+ <string name="pad_alt_label">Pad Alt</string>
</resources>
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.FragmentActivity;
-import android.support.v4.view.ViewPager;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.widget.TextView;
import android.widget.Toast;
import android.app.AlertDialog;
+import android.location.Location;
import org.altusmetrum.altoslib_1.*;
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;
// Intent request codes
private static final int REQUEST_CONNECT_DEVICE = 1;
private TextView mVersion;
// Tabs
- TabHost mTabHost;
- AltosViewPager mViewPager;
- TabsAdapter mTabsAdapter;
+ TabHost mTabHost;
+ AltosViewPager mViewPager;
+ TabsAdapter mTabsAdapter;
ArrayList<AltosDroidTab> mTabs = new ArrayList<AltosDroidTab>();
+ int tabHeight;
// Timer and Saved flight state for Age calculation
private Timer timer = new Timer();
AltosState saved_state;
+ Location saved_location;
// Service
private boolean mIsBound = false;
ad.mTitle.setText(R.string.title_connected_to);
ad.mTitle.append(str);
Toast.makeText(ad.getApplicationContext(), "Connected to " + str, Toast.LENGTH_SHORT).show();
- ad.mAltosVoice.speak("Connected");
break;
case TelemetryService.STATE_CONNECTING:
ad.mTitle.setText(R.string.title_connecting);
case MSG_TELEMETRY:
ad.update_ui((AltosState) msg.obj);
break;
+ case MSG_LOCATION:
+ ad.set_location((Location) msg.obj);
+ break;
+ case MSG_CRC_ERROR:
case MSG_UPDATE_AGE:
if (ad.saved_state != null) {
ad.mAgeView.setText(String.format("%d", (System.currentTimeMillis() - ad.saved_state.report_time + 500) / 1000));
mTabs.remove(mTab);
}
+ void set_location(Location location) {
+ saved_location = location;
+ update_ui(saved_state);
+ }
+
void update_ui(AltosState state) {
- if (saved_state != null) {
+ if (state != null && saved_state != null) {
if (saved_state.state != state.state) {
String currentTab = mTabHost.getCurrentTabTag();
switch (state.state) {
}
saved_state = state;
- mCallsignView.setText(state.data.callsign);
- mSerialView.setText(String.format("%d", state.data.serial));
- mFlightView.setText(String.format("%d", state.data.flight));
- mStateView.setText(state.data.state());
- mRSSIView.setText(String.format("%d", state.data.rssi));
+ 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 (state != null) {
+ mCallsignView.setText(state.data.callsign);
+ mSerialView.setText(String.format("%d", state.data.serial));
+ mFlightView.setText(String.format("%d", state.data.flight));
+ mStateView.setText(state.data.state());
+ mRSSIView.setText(String.format("%d", state.data.rssi));
+ }
for (AltosDroidTab mTab : mTabs)
- mTab.update_ui(state);
+ mTab.update_ui(state, from_receiver, saved_location);
- mAltosVoice.tell(state);
+ if (state != null)
+ mAltosVoice.tell(state);
}
private void onTimerTick() {
static String pos(double p, String pos, String neg) {
String h = pos;
+ if (p == AltosRecord.MISSING)
+ return "";
if (p < 0) {
h = neg;
p = -p;
}
int deg = (int) Math.floor(p);
double min = (p - Math.floor(p)) * 60.0;
- return String.format("%d° %9.6f\" %s", deg, min, h);
+ return String.format("%d°%9.4f\" %s", deg, min, h);
+ }
+
+ static String number(String format, double value) {
+ if (value == AltosRecord.MISSING)
+ return "";
+ return String.format(format, value);
+ }
+
+ static String integer(String format, int value) {
+ if (value == AltosRecord.MISSING)
+ return "";
+ return String.format(format, value);
}
@Override
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);
mTabHost.setup();
mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator("Map"), TabMap.class, null);
+ // Scale the size of the Tab bar for different screen densities
+ // This probably won't be needed when we start supporting ICS+ tabs.
+ DisplayMetrics metrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ int density = metrics.densityDpi;
+
+ if (density==DisplayMetrics.DENSITY_XHIGH)
+ tabHeight = 65;
+ else if (density==DisplayMetrics.DENSITY_HIGH)
+ tabHeight = 45;
+ else if (density==DisplayMetrics.DENSITY_MEDIUM)
+ tabHeight = 35;
+ else if (density==DisplayMetrics.DENSITY_LOW)
+ tabHeight = 25;
+ else
+ tabHeight = 65;
+
+ 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);
super.onDestroy();
if(D) Log.e(TAG, "--- ON DESTROY ---");
- mAltosVoice.stop();
+ if (mAltosVoice != null) mAltosVoice.stop();
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
package org.altusmetrum.AltosDroid;
-import org.altusmetrum.altoslib_1.AltosState;
+import org.altusmetrum.altoslib_1.*;
+import android.location.Location;
public interface AltosDroidTab {
- public void update_ui(AltosState state);
+ public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver);
}
public void onInit(int status) {\r
if (status == TextToSpeech.SUCCESS) tts_enabled = true;\r
if (tts_enabled) {\r
- speak("AltosDroid ready");\r
idle_thread = new IdleThread();\r
}\r
}\r
// If there are paired devices, add each one to the ArrayAdapter
if (pairedDevices.size() > 0) {
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
- for (BluetoothDevice device : pairedDevices) {
- mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
- }
+ for (BluetoothDevice device : pairedDevices)
+ if (device.getName().startsWith("TeleBT"))
+ mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+
} else {
String noDevices = getResources().getText(R.string.none_paired).toString();
mPairedDevicesArrayAdapter.add(noDevices);
// 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) {
+ if ( device.getBondState() != BluetoothDevice.BOND_BONDED
+ && device.getName().startsWith("TeleBT") ) {
mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
// When discovery is finished, change the Activity title
public class GoNoGoLights {
private Boolean state;
+ private Boolean missing;
+ private Boolean set;
private ImageView red;
private ImageView green;
red = in_red;
green = in_green;
state = false;
+ missing = true;
+ set = false;
dRed = r.getDrawable(R.drawable.redled);
dGreen = r.getDrawable(R.drawable.greenled);
dGray = r.getDrawable(R.drawable.grayled);
}
- public void set(Boolean s) {
- if (s == state) return;
+ public void set(Boolean s, Boolean m) {
+ if (set && s == state && m == missing) return;
state = s;
- if (state) {
+ missing = m;
+ set = true;
+ if (missing) {
+ red.setImageDrawable(dGray);
+ green.setImageDrawable(dGray);
+ } else if (state) {
red.setImageDrawable(dGray);
green.setImageDrawable(dGreen);
} else {
package org.altusmetrum.AltosDroid;
-import org.altusmetrum.altoslib_1.AltosState;
+import org.altusmetrum.altoslib_1.*;
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import android.location.Location;
public class TabAscent extends Fragment implements AltosDroidTab {
AltosDroid mAltosDroid;
mAltosDroid = null;
}
- public void update_ui(AltosState state) {
- mHeightView.setText(String.format("%6.0f m", state.height));
- mMaxHeightView.setText(String.format("%6.0f m", state.max_height));
- mSpeedView.setText(String.format("%6.0f m/s", state.speed()));
- mMaxSpeedView.setText(String.format("%6.0f m/s", state.max_speed()));
- mAccelView.setText(String.format("%6.0f m/s²", state.acceleration));
- mMaxAccelView.setText(String.format("%6.0f m/s²", state.max_acceleration));
-
- mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
- mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
-
- mApogeeVoltageView.setText(String.format("%4.2f V", state.drogue_sense));
- mApogeeLights.set(state.drogue_sense > 3.2);
-
- mMainVoltageView.setText(String.format("%4.2f V", state.main_sense));
- mMainLights.set(state.main_sense > 3.2);
+ public void update_ui(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));
+
+ if (state.gps != null) {
+ mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+ mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+ } else {
+ mLatitudeView.setText("");
+ mLongitudeView.setText("");
+ }
+
+ mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.drogue_sense));
+ mApogeeLights.set(state.drogue_sense > 3.2, state.drogue_sense == AltosRecord.MISSING);
+
+ mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_sense));
+ mMainLights.set(state.main_sense > 3.2, state.main_sense == AltosRecord.MISSING);
+ }
}
}
package org.altusmetrum.AltosDroid;
-import org.altusmetrum.altoslib_1.AltosGreatCircle;
-import org.altusmetrum.altoslib_1.AltosState;
+import org.altusmetrum.altoslib_1.*;
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import android.location.Location;
public class TabDescent extends Fragment implements AltosDroidTab {
AltosDroid mAltosDroid;
mAltosDroid = null;
}
- public void update_ui(AltosState state) {
- mSpeedView.setText(String.format("%6.0f m/s", state.speed()));
- mHeightView.setText(String.format("%6.0f m", state.height));
- mElevationView.setText(String.format("%3.0f°", state.elevation));
- mRangeView.setText(String.format("%6.0f m", state.range));
- if (state.from_pad != null) {
- mBearingView.setText(String.format("%3.0f°", state.from_pad.bearing));
- mCompassView.setText(state.from_pad.bearing_words(AltosGreatCircle.BEARING_LONG));
+ public void update_ui(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));
+ if (from_receiver != null) {
+ mElevationView.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
+ mRangeView.setText(AltosDroid.number("%6.0f m", 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));
+ } else {
+ mElevationView.setText("<unknown>");
+ mRangeView.setText("<unknown>");
+ mBearingView.setText("<unknown>");
+ mCompassView.setText("<unknown>");
+ mDistanceView.setText("<unknown>");
+ }
+ if (state.gps != null) {
+ mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+ mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+ }
+
+ mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.drogue_sense));
+ mApogeeLights.set(state.drogue_sense > 3.2, state.drogue_sense == AltosRecord.MISSING);
+
+ mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_sense));
+ mMainLights.set(state.main_sense > 3.2, state.main_sense == AltosRecord.MISSING);
}
- mDistanceView.setText(String.format("%6.0f m", state.range));
- mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
- mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
-
- mApogeeVoltageView.setText(String.format("%4.2f V", state.drogue_sense));
- mApogeeLights.set(state.drogue_sense > 3.2);
-
- mMainVoltageView.setText(String.format("%4.2f V", state.main_sense));
- mMainLights.set(state.main_sense > 3.2);
}
}
package org.altusmetrum.AltosDroid;
-import org.altusmetrum.altoslib_1.AltosState;
+import org.altusmetrum.altoslib_1.*;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import android.location.Location;
public class TabLanded extends Fragment implements AltosDroidTab {
AltosDroid mAltosDroid;
private TextView mBearingView;
private TextView mDistanceView;
- private TextView mLatitudeView;
- private TextView mLongitudeView;
+ private TextView mTargetLatitudeView;
+ private TextView mTargetLongitudeView;
+ private TextView mReceiverLatitudeView;
+ private TextView mReceiverLongitudeView;
private TextView mMaxHeightView;
private TextView mMaxSpeedView;
private TextView mMaxAccelView;
mBearingView = (TextView) v.findViewById(R.id.bearing_value);
mDistanceView = (TextView) v.findViewById(R.id.distance_value);
- mLatitudeView = (TextView) v.findViewById(R.id.lat_value);
- mLongitudeView = (TextView) v.findViewById(R.id.lon_value);
+ mTargetLatitudeView = (TextView) v.findViewById(R.id.target_lat_value);
+ mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
+ mReceiverLatitudeView = (TextView) v.findViewById(R.id.receiver_lat_value);
+ mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
mMaxSpeedView = (TextView) v.findViewById(R.id.max_speed_value);
mMaxAccelView = (TextView) v.findViewById(R.id.max_accel_value);
mAltosDroid = null;
}
- public void update_ui(AltosState state) {
- if (state.from_pad != null) {
- mBearingView.setText(String.format("%3.0f°", state.from_pad.bearing));
- mDistanceView.setText(String.format("%6.0f m", state.from_pad.distance));
+ public void update_ui(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));
+ }
+ if (state != null && state.gps != null) {
+ mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+ mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+ }
+
+ if (receiver != null) {
+ mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+ mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "W", "E"));
+ }
+
+ 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()));
}
- mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
- mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
- 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()));
}
}
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
+//import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import android.location.Location;
public class TabMap extends Fragment implements AltosDroidTab {
AltosDroid mAltosDroid;
private TextView mDistanceView;
private TextView mBearingView;
- private TextView mLatitudeView;
- private TextView mLongitudeView;
+ private TextView mTargetLatitudeView;
+ private TextView mTargetLongitudeView;
+ private TextView mReceiverLatitudeView;
+ private TextView mReceiverLongitudeView;
+
+ private double mapAccuracy = -1;
@Override
public void onAttach(Activity activity) {
View v = inflater.inflate(R.layout.tab_map, container, false);
mDistanceView = (TextView)v.findViewById(R.id.distance_value);
mBearingView = (TextView)v.findViewById(R.id.bearing_value);
- mLatitudeView = (TextView)v.findViewById(R.id.lat_value);
- mLongitudeView = (TextView)v.findViewById(R.id.lon_value);
+ mTargetLatitudeView = (TextView)v.findViewById(R.id.target_lat_value);
+ mTargetLongitudeView = (TextView)v.findViewById(R.id.target_lon_value);
+ mReceiverLatitudeView = (TextView)v.findViewById(R.id.receiver_lat_value);
+ mReceiverLongitudeView = (TextView)v.findViewById(R.id.receiver_lon_value);
return v;
}
mMap.setMyLocationEnabled(true);
mMap.getUiSettings().setTiltGesturesEnabled(false);
mMap.getUiSettings().setZoomControlsEnabled(false);
- mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40.8,-104.7),8));
mRocketMarker = mMap.addMarker(
// From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
}
}
- public void update_ui(AltosState state) {
- if (state.from_pad != null) {
- mDistanceView.setText(String.format("%6.0f m", state.from_pad.distance));
- mBearingView.setText(String.format("%3.0f°", state.from_pad.bearing));
+ private void center(double lat, double lon, double accuracy) {
+ if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
+ mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
+ mapAccuracy = accuracy;
}
- mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
- mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+ }
- if (mapLoaded) {
- mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon));
- mRocketMarker.setVisible(true);
+ public void update_ui(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));
+ }
- mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon)));
- mPolyline.setVisible(true);
+ if (state != null) {
+ if (mapLoaded) {
+ if (state.gps != null) {
+ mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon));
+ mRocketMarker.setVisible(true);
- if (state.state == AltosLib.ao_flight_pad) {
- mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
- mPadMarker.setVisible(true);
+ mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon)));
+ mPolyline.setVisible(true);
+ }
+
+ if (state.state == AltosLib.ao_flight_pad) {
+ mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
+ mPadMarker.setVisible(true);
+ }
+ }
+ if (state.gps != null) {
+ mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+ mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "W", "E"));
+ if (state.gps.locked && state.gps.nsat >= 4)
+ center (state.gps.lat, state.gps.lon, 10);
}
}
+
+ if (receiver != null) {
+ double accuracy;
+
+ if (receiver.hasAccuracy())
+ accuracy = receiver.getAccuracy();
+ else
+ accuracy = 1000;
+ mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+ mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "W", "E"));
+ center (receiver.getLatitude(), receiver.getLongitude(), accuracy);
+ }
+
}
}
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import android.location.Location;
public class TabPad extends Fragment implements AltosDroidTab {
AltosDroid mAltosDroid;
mAltosDroid = null;
}
- public void update_ui(AltosState state) {
- mBatteryVoltageView.setText(String.format("%4.2f V", state.battery));
- mBatteryLights.set(state.battery > 3.7);
-
- mApogeeVoltageView.setText(String.format("%4.2f V", state.drogue_sense));
- mApogeeLights.set(state.drogue_sense > 3.2);
-
- mMainVoltageView.setText(String.format("%4.2f V", state.main_sense));
- mMainLights.set(state.main_sense > 3.2);
-
- if (state.data.flight != 0) {
- if (state.data.state <= AltosLib.ao_flight_pad)
- mDataLoggingView.setText("Ready to record");
- else if (state.data.state < AltosLib.ao_flight_landed)
- mDataLoggingView.setText("Recording data");
- else
- mDataLoggingView.setText("Recorded data");
- } else {
- mDataLoggingView.setText("Storage full");
+ public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+ if (state != null) {
+ mBatteryVoltageView.setText(AltosDroid.number("%4.2f V", state.battery));
+ mBatteryLights.set(state.battery > 3.7, state.battery == AltosRecord.MISSING);
+
+ mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.drogue_sense));
+ mApogeeLights.set(state.drogue_sense > 3.2, state.drogue_sense == AltosRecord.MISSING);
+
+ mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_sense));
+ mMainLights.set(state.main_sense > 3.2, state.main_sense == AltosRecord.MISSING);
+
+ if (state.data.flight != 0) {
+ if (state.data.state <= AltosLib.ao_flight_pad)
+ mDataLoggingView.setText("Ready to record");
+ else if (state.data.state < AltosLib.ao_flight_landed)
+ mDataLoggingView.setText("Recording data");
+ else
+ mDataLoggingView.setText("Recorded data");
+ } else {
+ mDataLoggingView.setText("Storage full");
+ }
+ mDataLoggingLights.set(state.data.flight != 0, state.data.flight == AltosRecord.MISSING);
+
+ if (state.gps != null) {
+ mGPSLockedView.setText(AltosDroid.integer("%4d sats", state.gps.nsat));
+ mGPSLockedLights.set(state.gps.locked && state.gps.nsat >= 4, false);
+ if (state.gps_ready)
+ mGPSReadyView.setText("Ready");
+ else
+ mGPSReadyView.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
+ } else
+ mGPSLockedLights.set(false, true);
+ mGPSReadyLights.set(state.gps_ready, state.gps == null);
}
- mDataLoggingLights.set(state.data.flight != 0);
- mGPSLockedView.setText(String.format("%4d sats", state.gps.nsat));
- mGPSLockedLights.set(state.gps.locked && state.gps.nsat >= 4);
-
- if (state.gps_ready)
- mGPSReadyView.setText("Ready");
- else
- mGPSReadyView.setText(String.format("Waiting %d", state.gps_waiting));
- mGPSReadyLights.set(state.gps_ready);
-
- mPadLatitudeView.setText(AltosDroid.pos(state.pad_lat, "N", "S"));
- mPadLongitudeView.setText(AltosDroid.pos(state.pad_lon, "W", "E"));
- mPadAltitudeView.setText(String.format("%4.0f m", state.pad_alt));
+ if (receiver != null) {
+ double altitude = 0;
+ 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));
+ }
}
}
if (record == null)\r
break;\r
state = new AltosState(record, state);\r
-\r
handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();\r
} catch (ParseException pp) {\r
Log.e(TAG, String.format("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage()));\r
} catch (AltosCRCException ce) {\r
++crc_errors;\r
+ handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget();\r
}\r
}\r
} catch (InterruptedException ee) {\r
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
-//import android.os.Bundle;
+import android.content.Context;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationListener;
import org.altusmetrum.altoslib_1.*;
-public class TelemetryService extends Service {
+
+public class TelemetryService extends Service implements LocationListener {
private static final String TAG = "TelemetryService";
private static final boolean D = true;
static final int MSG_DISCONNECTED = 6;
static final int MSG_TELEMETRY = 7;
static final int MSG_SETFREQUENCY = 8;
+ static final int MSG_CRC_ERROR = 9;
public static final int STATE_NONE = 0;
public static final int STATE_READY = 1;
// internally track state of bluetooth connection
private int state = STATE_NONE;
+ // 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;
// 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));
} catch (RemoteException e) {
s.mClients.remove(msg.replyTo);
}
}
break;
case MSG_TELEMETRY:
+ // forward telemetry messages
+ s.last_state = (AltosState) msg.obj;
s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_TELEMETRY, msg.obj));
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));
+ break;
case MSG_SETFREQUENCY:
if (s.state == STATE_CONNECTED) {
try {
}
private void startAltosBluetooth() {
+ if (device == null) {
+ return;
+ }
if (mAltosBluetooth == null) {
if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()));
mAltosBluetooth = new AltosBluetooth(device, mHandler);
private void connected() {
try {
+ if (mAltosBluetooth == null)
+ throw new InterruptedException("no bluetooth");
mConfigData = mAltosBluetooth.config_data();
} catch (InterruptedException e) {
} catch (TimeoutException e) {
// 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, 0, 0, this);
+ locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
}
@Override
@Override
public void onDestroy() {
+ // Stop listening for location updates
+ ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
+
// Stop the bluetooth Comms threads
stopAltosBluetooth();
}
+ public void onLocationChanged(Location location) {
+ last_location = location;
+ sendMessageToClients(Message.obtain(null, AltosDroid.MSG_LOCATION, location));
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+
+ public void onProviderEnabled(String provider) {
+ }
+
+ public void onProviderDisabled(String provider) {
+ }
+
}
}
}
+ public boolean has_monitor_battery() {
+ if (product.startsWith("TeleBT"))
+ return true;
+ return false;
+ }
+
int[] parse_version(String v) {
String[] parts = v.split("\\.");
int r[] = new int[parts.length];
/* UI doesn't support AES key config */
/* AO_PYRO_NUM */
- if (pyros.length > 0) {
+ if (npyro > 0) {
for (int p = 0; p < pyros.length; p++) {
link.printf("c P %s\n",
pyros[p].toString());
switch (log_format) {
case AltosLib.AO_LOG_FORMAT_FULL:
case AltosLib.AO_LOG_FORMAT_TINY:
- case AltosLib.AO_LOG_FORMAT_MEGAMETRUM:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA:
link.printf("l\n");
read_link(link, "done");
default:
case AltosLib.AO_LOG_GPS_LAT:
eeprom.seen |= AltosRecord.seen_gps_lat;
int lat32 = record.a | (record.b << 16);
+ if (state.gps == null)
+ state.gps = new AltosGPS();
state.gps.lat = (double) lat32 / 1e7;
break;
case AltosLib.AO_LOG_GPS_LON:
eeprom.seen |= AltosRecord.seen_gps_lon;
int lon32 = record.a | (record.b << 16);
+ if (state.gps == null)
+ state.gps = new AltosGPS();
state.gps.lon = (double) lon32 / 1e7;
break;
case AltosLib.AO_LOG_GPS_ALT:
+ if (state.gps == null)
+ state.gps = new AltosGPS();
state.gps.alt = record.a;
break;
case AltosLib.AO_LOG_GPS_SAT:
if (state.tick == eeprom.gps_tick) {
int svid = record.a;
int c_n0 = record.b >> 8;
+ if (state.gps == null)
+ state.gps = new AltosGPS();
state.gps.add_sat(svid, c_n0);
}
break;
case AltosLib.AO_LOG_GPS_DATE:
+ if (state.gps == null)
+ state.gps = new AltosGPS();
state.gps.year = (record.a & 0xff) + 2000;
state.gps.month = record.a >> 8;
state.gps.day = record.b & 0xff;
public boolean supports_telemetry(int telemetry) { return false; }
public File backing_file() { return null; }
+
+ public boolean has_monitor_battery() { return false; }
+
+ public double monitor_battery() { return AltosRecord.MISSING; }
}
}
public AltosGPS(AltosGPS old) {
- nsat = old.nsat;
- locked = old.locked;
- connected = old.connected;
- lat = old.lat; /* degrees (+N -S) */
- lon = old.lon; /* degrees (+E -W) */
- alt = old.alt; /* m */
- year = old.year;
- month = old.month;
- day = old.day;
- hour = old.hour;
- minute = old.minute;
- second = old.second;
-
- ground_speed = old.ground_speed; /* m/s */
- course = old.course; /* degrees */
- climb_rate = old.climb_rate; /* m/s */
- hdop = old.hdop; /* unitless? */
- h_error = old.h_error; /* m */
- v_error = old.v_error; /* m */
-
- if (old.cc_gps_sat != null) {
- cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
- for (int i = 0; i < old.cc_gps_sat.length; i++) {
- cc_gps_sat[i] = new AltosGPSSat();
- cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
- cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
+ if (old != null) {
+ nsat = old.nsat;
+ locked = old.locked;
+ connected = old.connected;
+ lat = old.lat; /* degrees (+N -S) */
+ lon = old.lon; /* degrees (+E -W) */
+ alt = old.alt; /* m */
+ year = old.year;
+ month = old.month;
+ day = old.day;
+ hour = old.hour;
+ minute = old.minute;
+ second = old.second;
+
+ ground_speed = old.ground_speed; /* m/s */
+ course = old.course; /* degrees */
+ climb_rate = old.climb_rate; /* m/s */
+ hdop = old.hdop; /* unitless? */
+ h_error = old.h_error; /* m */
+ v_error = old.v_error; /* m */
+
+ if (old.cc_gps_sat != null) {
+ cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
+ for (int i = 0; i < old.cc_gps_sat.length; i++) {
+ cc_gps_sat[i] = new AltosGPSSat();
+ cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
+ cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
+ }
}
+ } else {
+ ClearGPSTime();
+ cc_gps_sat = null;
}
}
}
public class AltosGreatCircle {
public double distance;
public double bearing;
+ public double range;
+ public double elevation;
double sqr(double a) { return a * a; }
return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16];
}
- public AltosGreatCircle (double start_lat, double start_lon,
- double end_lat, double end_lon)
- {
+ public AltosGreatCircle (double start_lat, double start_lon, double start_alt,
+ double end_lat, double end_lon, double end_alt) {
double lat1 = rad * start_lat;
double lon1 = rad * -start_lon;
double lat2 = rad * end_lat;
}
distance = d * earth_radius;
bearing = course * 180/Math.PI;
+
+ double height_diff = end_alt - start_alt;
+ range = Math.sqrt(distance * distance + height_diff * height_diff);
+ elevation = Math.atan2(height_diff, distance) * 180 / Math.PI;
+ }
+
+ public AltosGreatCircle (double start_lat, double start_lon,
+ double end_lat, double end_lon) {
+ this(start_lat, start_lon, 0, end_lat, end_lon, 0);
}
public AltosGreatCircle(AltosGPS start, AltosGPS end) {
- this(start.lat, start.lon, end.lat, end.lon);
+ this(start.lat, start.lon, start.alt, end.lat, end.lon, end.alt);
}
public AltosGreatCircle() {
distance = 0;
bearing = 0;
+ range = 0;
+ elevation = 0;
}
}
double frequency;
String callsign;
AltosState previous_state;
+ AltosListenerState listener_state;
AltosConfigData config_data;
AltosGPS gps;
}
boolean has_sensor_mm(AltosConfigData config_data) {
- return config_data.product.startsWith("MegaMetrum");
+ return config_data.product.startsWith("TeleMega");
}
boolean has_gps(AltosConfigData config_data) {
- return config_data.product.startsWith("TeleMetrum") || config_data.product.startsWith("MegaMetrum");
+ return config_data.product.startsWith("TeleMetrum") || config_data.product.startsWith("TeleMega");
}
AltosRecord sensor_mm(AltosConfigData config_data) throws InterruptedException, TimeoutException {
} finally {
if (remote) {
link.stop_remote();
- if (record != null)
- record.rssi = AltosRSSI();
+ if (record != null) {
+ record.rssi = link.rssi();
+ listener_state.battery = link.monitor_battery();
+ }
} else {
if (record != null)
record.rssi = 0;
}
public void post_state() {
- listener.update(state);
+ listener.update(state, listener_state);
}
public void abort() {
link = in_link;
remote = in_remote;
state = null;
+ listener_state = new AltosListenerState();
}
}
package org.altusmetrum.altoslib_1;
public interface AltosIdleMonitorListener {
- public void update(AltosState state);
+ public void update(AltosState state, AltosListenerState listener_state);
}
\ No newline at end of file
public static final int AO_LOG_SERIAL_NUMBER = 2002;
public static final int AO_LOG_LOG_FORMAT = 2003;
- /* Added for header fields in megametrum files */
+ /* Added for header fields in telemega files */
public static final int AO_LOG_BARO_RESERVED = 3000;
public static final int AO_LOG_BARO_SENS = 3001;
public static final int AO_LOG_BARO_OFF = 3002;
public final static int product_telelco = 0x0010;
public final static int product_telescience = 0x0011;
public final static int product_telepyro =0x0012;
- public final static int product_megametrum = 0x0023;
+ public final static int product_telemega = 0x0023;
public final static int product_megadongle = 0x0024;
+ public final static int product_telegps = 0x0025;
public final static int product_altusmetrum_min = 0x000a;
- public final static int product_altusmetrum_max = 0x0024;
+ public final static int product_altusmetrum_max = 0x0025;
public final static int product_any = 0x10000;
public final static int product_basestation = 0x10000 + 1;
public static final int AO_LOG_FORMAT_TINY = 2;
public static final int AO_LOG_FORMAT_TELEMETRY = 3;
public static final int AO_LOG_FORMAT_TELESCIENCE = 4;
- public static final int AO_LOG_FORMAT_MEGAMETRUM = 5;
+ public static final int AO_LOG_FORMAT_TELEMEGA = 5;
public static final int AO_LOG_FORMAT_NONE = 127;
public static boolean isspace(int c) {
remote = false;
}
+ public int rssi() throws TimeoutException, InterruptedException {
+ if (remote)
+ return 0;
+ printf("s\n");
+ String line = get_reply_no_dialog(5000);
+ if (line == null)
+ throw new TimeoutException();
+ String[] items = line.split("\\s+");
+ if (items.length < 2)
+ return 0;
+ if (!items[0].equals("RSSI:"))
+ return 0;
+ int rssi = Integer.parseInt(items[1]);
+ return rssi;
+ }
+
+ public String[] adc() throws TimeoutException, InterruptedException {
+ printf("a\n");
+ for (;;) {
+ String line = get_reply_no_dialog(5000);
+ if (line == null) {
+ throw new TimeoutException();
+ }
+ if (!line.startsWith("tick:"))
+ continue;
+ String[] items = line.split("\\s+");
+ return items;
+ }
+ }
+
+ public boolean has_monitor_battery() {
+ return config_data.has_monitor_battery();
+ }
+
+ public double monitor_battery() {
+ int monitor_batt = AltosRecord.MISSING;
+
+ if (config_data.has_monitor_battery()) {
+ try {
+ String[] items = adc();
+ for (int i = 0; i < items.length;) {
+ if (items[i].equals("batt")) {
+ monitor_batt = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ i++;
+ }
+ } catch (InterruptedException ie) {
+ } catch (TimeoutException te) {
+ }
+ }
+ if (monitor_batt == AltosRecord.MISSING)
+ return AltosRecord.MISSING;
+ return AltosConvert.cc_battery_to_voltage(monitor_batt);
+ }
+
public AltosLink() {
callsign = "";
}
--- /dev/null
+/*
+ * Copyright © 2013 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.
+ */
+
+package org.altusmetrum.altoslib_1;
+
+public class AltosListenerState {
+ public int crc_errors;
+ public double battery;
+
+ public AltosListenerState() {
+ crc_errors = 0;
+ battery = AltosRecord.MISSING;
+ }
+}
package org.altusmetrum.altoslib_1;
-public abstract class AltosRecord implements Comparable <AltosRecord>, Cloneable {
+public class AltosRecord implements Comparable <AltosRecord>, Cloneable {
public static final int seen_flight = 1;
public static final int seen_sensor = 2;
* temperature: °C
*/
- abstract public double pressure();
- abstract public double ground_pressure();
- abstract public double acceleration();
+ public double pressure() { return MISSING; }
+ public double ground_pressure() { return MISSING; }
+ public double acceleration() { return MISSING; }
public double altitude() {
double p = pressure();
return tick - o.tick;
}
- abstract public AltosRecord clone();
+ public AltosRecord clone() {
+ AltosRecord n = new AltosRecord();
+ n.copy(this);
+ return n;
+ }
public void copy(AltosRecord old) {
seen = old.seen;
seen = 0;
version = 0;
callsign = "N0CALL";
- serial = 0;
- flight = 0;
+ serial = MISSING;
+ flight = MISSING;
rssi = 0;
status = 0;
state = AltosLib.ao_flight_startup;
tick = 0;
- gps = new AltosGPS();
+ gps = null;
new_gps = false;
companion = null;
int accel_ref;
public AltosSensorMM(AltosLink link) throws InterruptedException, TimeoutException {
- link.printf("a\n");
- for (;;) {
- String line = link.get_reply_no_dialog(5000);
- if (line == null) {
- throw new TimeoutException();
+ String[] items = link.adc();
+ sense = new int[6];
+ for (int i = 0; i < items.length;) {
+ if (items[i].equals("tick:")) {
+ tick = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("0:")) {
+ sense[0] = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("1:")) {
+ sense[1] = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("2:")) {
+ sense[2] = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("3:")) {
+ sense[3] = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("4:")) {
+ sense[4] = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("5:")) {
+ sense[5] = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("6:")) {
+ v_batt = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("7:")) {
+ v_pyro = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("8:")) {
+ accel = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
}
- if (!line.startsWith("tick:"))
+ if (items[i].equals("9:")) {
+ accel_ref = Integer.parseInt(items[i+1]);
+ i += 2;
continue;
- String[] items = line.split("\\s+");
- sense = new int[6];
- for (int i = 0; i < items.length;) {
- if (items[i].equals("tick:")) {
- tick = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("0:")) {
- sense[0] = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("1:")) {
- sense[1] = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("2:")) {
- sense[2] = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("3:")) {
- sense[3] = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("4:")) {
- sense[4] = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("5:")) {
- sense[5] = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("6:")) {
- v_batt = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("7:")) {
- v_pyro = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("8:")) {
- accel = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("9:")) {
- accel_ref = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- i++;
}
- break;
+ i++;
}
}
}
public AltosSensorTM(AltosLink link, AltosConfigData config_data) throws InterruptedException, TimeoutException {
super();
- link.printf("a\n");
- for (;;) {
- String line = link.get_reply_no_dialog(5000);
- if (line == null) {
- throw new TimeoutException();
+ String[] items = link.adc();
+ for (int i = 0; i < items.length;) {
+ if (items[i].equals("tick:")) {
+ tick = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("accel:")) {
+ accel = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("pres:")) {
+ pres = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("temp:")) {
+ temp = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("batt:")) {
+ batt = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("drogue:")) {
+ drogue = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
}
- if (!line.startsWith("tick:"))
+ if (items[i].equals("main:")) {
+ main = Integer.parseInt(items[i+1]);
+ i += 2;
continue;
- String[] items = line.split("\\s+");
- for (int i = 0; i < items.length;) {
- if (items[i].equals("tick:")) {
- tick = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("accel:")) {
- accel = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("pres:")) {
- pres = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("temp:")) {
- temp = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("batt:")) {
- batt = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("drogue:")) {
- drogue = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- if (items[i].equals("main:")) {
- main = Integer.parseInt(items[i+1]);
- i += 2;
- continue;
- }
- i++;
}
- break;
+ i++;
}
ground_accel = config_data.accel_cal_plus;
ground_pres = pres;
ground_altitude = data.ground_altitude();
altitude = data.altitude();
+ if (altitude == AltosRecord.MISSING && data.gps != null)
+ altitude = data.gps.alt;
+ height = AltosRecord.MISSING;
if (data.kalman_height != AltosRecord.MISSING)
height = data.kalman_height;
else {
- if (prev_state != null)
- height = (prev_state.height * 15 + altitude - ground_altitude) / 16.0;
+ if (altitude != AltosRecord.MISSING && ground_altitude != AltosRecord.MISSING) {
+ double cur_height = altitude - ground_altitude;
+ if (prev_state == null || prev_state.height == AltosRecord.MISSING)
+ height = cur_height;
+ else
+ height = (prev_state.height * 15 + cur_height) / 16.0;
+ }
}
report_time = System.currentTimeMillis();
/* compute barometric speed */
double height_change = height - prev_state.height;
+
+ double prev_baro_speed = prev_state.baro_speed;
+ if (prev_baro_speed == AltosRecord.MISSING)
+ prev_baro_speed = 0;
+
if (time_change > 0)
- baro_speed = (prev_state.baro_speed * 3 + (height_change / time_change)) / 4.0;
+ baro_speed = (prev_baro_speed * 3 + (height_change / time_change)) / 4.0;
else
baro_speed = prev_state.baro_speed;
+ double prev_accel_speed = prev_state.accel_speed;
+
+ if (prev_accel_speed == AltosRecord.MISSING)
+ prev_accel_speed = 0;
+
if (acceleration == AltosRecord.MISSING) {
/* Fill in mising acceleration value */
accel_speed = baro_speed;
- if (time_change > 0)
- acceleration = (accel_speed - prev_state.accel_speed) / time_change;
+
+ if (time_change > 0 && accel_speed != AltosRecord.MISSING)
+ acceleration = (accel_speed - prev_accel_speed) / time_change;
else
acceleration = prev_state.acceleration;
} else {
/* compute accelerometer speed */
- accel_speed = prev_state.accel_speed + acceleration * time_change;
+ accel_speed = prev_accel_speed + acceleration * time_change;
}
}
-
} else {
npad = 0;
ngps = 0;
gps = null;
- baro_speed = 0;
- accel_speed = 0;
+ baro_speed = AltosRecord.MISSING;
+ accel_speed = AltosRecord.MISSING;
+ pad_alt = AltosRecord.MISSING;
+ max_baro_speed = 0;
+ max_accel_speed = 0;
+ max_height = 0;
+ max_acceleration = 0;
time_change = 0;
- if (acceleration == AltosRecord.MISSING)
- acceleration = 0;
}
time = tick / 100.0;
- if (cur.new_gps && (state == AltosLib.ao_flight_pad || state == AltosLib.ao_flight_idle)) {
+ if (cur.new_gps && (state < AltosLib.ao_flight_boost)) {
/* Track consecutive 'good' gps reports, waiting for 10 of them */
if (data.gps != null && data.gps.locked && data.gps.nsat >= 4)
/* Average GPS data while on the pad */
if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) {
- if (ngps > 1) {
+ if (ngps > 1 && state == AltosLib.ao_flight_pad) {
/* filter pad position */
pad_lat = (pad_lat * 31.0 + data.gps.lat) / 32.0;
pad_lon = (pad_lon * 31.0 + data.gps.lon) / 32.0;
ngps++;
}
} else {
- if (ngps == 0)
+ if (ngps == 0 && ground_altitude != AltosRecord.MISSING)
pad_alt = ground_altitude;
}
boost = (AltosLib.ao_flight_boost == state);
/* Only look at accelerometer data under boost */
- if (boost && acceleration > max_acceleration && acceleration != AltosRecord.MISSING)
+ if (boost && acceleration != AltosRecord.MISSING && (max_acceleration == AltosRecord.MISSING || acceleration > max_acceleration))
max_acceleration = acceleration;
- if (boost && accel_speed > max_accel_speed && accel_speed != AltosRecord.MISSING)
+ if (boost && accel_speed != AltosRecord.MISSING && accel_speed > max_accel_speed)
max_accel_speed = accel_speed;
- if (boost && baro_speed > max_baro_speed && baro_speed != AltosRecord.MISSING)
+ if (boost && baro_speed != AltosRecord.MISSING && baro_speed > max_baro_speed)
max_baro_speed = baro_speed;
- if (height > max_height && height != AltosRecord.MISSING)
+ if (height != AltosRecord.MISSING && height > max_height)
max_height = height;
+ elevation = 0;
+ range = -1;
+ gps_height = 0;
if (data.gps != null) {
if (gps == null || !gps.locked || data.gps.locked)
gps = data.gps;
if (ngps > 0 && gps.locked) {
- from_pad = new AltosGreatCircle(pad_lat, pad_lon, gps.lat, gps.lon);
- }
- }
- elevation = 0;
- range = -1;
- if (ngps > 0) {
- gps_height = gps.alt - pad_alt;
- if (from_pad != null) {
- elevation = Math.atan2(height, from_pad.distance) * 180 / Math.PI;
- range = Math.sqrt(height * height + from_pad.distance * from_pad.distance);
+ double h = height;
+
+ if (h == AltosRecord.MISSING) h = 0;
+ from_pad = new AltosGreatCircle(pad_lat, pad_lon, 0, gps.lat, gps.lon, h);
+ elevation = from_pad.elevation;
+ range = from_pad.range;
+ gps_height = gps.alt - pad_alt;
}
- } else {
- gps_height = 0;
}
}
return log.file();
}
+ public boolean has_monitor_battery() {
+ return link.has_monitor_battery();
+ }
+
+ public double monitor_battery() {
+ return link.monitor_battery();
+ }
+
public AltosTelemetryReader (AltosLink in_link)
throws IOException, InterruptedException, TimeoutException {
link = in_link;
r = new AltosTelemetryRecordSensor(bytes, rssi);
break;
case packet_type_configuration:
- r = new AltosTelemetryRecordConfiguration(bytes);
+ r = new AltosTelemetryRecordConfiguration(bytes, rssi);
break;
case packet_type_location:
- r = new AltosTelemetryRecordLocation(bytes);
+ r = new AltosTelemetryRecordLocation(bytes, rssi);
break;
case packet_type_satellite:
- r = new AltosTelemetryRecordSatellite(bytes);
+ r = new AltosTelemetryRecordSatellite(bytes, rssi);
break;
case packet_type_companion:
- r = new AltosTelemetryRecordCompanion(bytes);
+ r = new AltosTelemetryRecordCompanion(bytes, rssi);
break;
case packet_type_MM_sensor:
r = new AltosTelemetryRecordMegaSensor(bytes, rssi);
break;
case packet_type_MM_data:
- r = new AltosTelemetryRecordMegaData(bytes);
+ r = new AltosTelemetryRecordMegaData(bytes, rssi);
break;
default:
- r = new AltosTelemetryRecordRaw(bytes);
+ r = new AltosTelemetryRecordRaw(bytes, rssi);
break;
}
break;
AltosRecordCompanion companion;
- public AltosTelemetryRecordCompanion(int[] in_bytes) {
- super(in_bytes);
+ public AltosTelemetryRecordCompanion(int[] in_bytes, int rssi) {
+ super(in_bytes, rssi);
int off = 0;
if (uint8(6) == 0)
String callsign;
String version;
- public AltosTelemetryRecordConfiguration(int[] in_bytes) {
- super(in_bytes);
+ public AltosTelemetryRecordConfiguration(int[] in_bytes, int rssi) {
+ super(in_bytes, rssi);
device_type = uint8(5);
flight = uint16(6);
int climb_rate;
int course;
- public AltosTelemetryRecordLocation(int[] in_bytes) {
- super(in_bytes);
+ public AltosTelemetryRecordLocation(int[] in_bytes, int rssi) {
+ super(in_bytes, rssi);
flags = uint8(5);
altitude = int16(6);
int speed;
int height;
- public AltosTelemetryRecordMegaData(int[] in_bytes) {
- super(in_bytes);
+ public AltosTelemetryRecordMegaData(int[] in_bytes, int rssi) {
+ super(in_bytes, rssi);
state = int8(5);
int mag_y;
int mag_z;
- int rssi;
-
- public AltosTelemetryRecordMegaSensor(int[] in_bytes, int in_rssi) {
- super(in_bytes);
+ public AltosTelemetryRecordMegaSensor(int[] in_bytes, int rssi) {
+ super(in_bytes, rssi);
accel = int16(6);
pres = int32(8);
mag_x = int16(26);
mag_y = int16(28);
mag_z = int16(30);
-
- rssi = in_rssi;
}
public AltosRecord update_state(AltosRecord previous) {
next.mag.y = mag_y;
next.mag.z = mag_z;
- next.rssi = rssi;
-
next.seen |= AltosRecord.seen_sensor;
return next;
int serial;
int tick;
int type;
+ int rssi;
long received_time;
return AltosLib.string(bytes, off + 1, l);
}
- public AltosTelemetryRecordRaw(int[] in_bytes) {
+ public AltosTelemetryRecordRaw(int[] in_bytes, int in_rssi) {
bytes = in_bytes;
serial = uint16(0);
tick = uint16(2);
type = uint8(4);
+ rssi = in_rssi;
}
public AltosRecord update_state(AltosRecord previous) {
next = new AltosRecordNone();
next.serial = serial;
next.tick = tick;
+ next.rssi = rssi;
return next;
}
int channels;
AltosGPSSat[] sats;
- public AltosTelemetryRecordSatellite(int[] in_bytes) {
- super(in_bytes);
+ public AltosTelemetryRecordSatellite(int[] in_bytes, int rssi) {
+ super(in_bytes, rssi);
channels = uint8(5);
if (channels > 12)
int accel_plus_g;
int accel_minus_g;
- int rssi;
-
- public AltosTelemetryRecordSensor(int[] in_bytes, int in_rssi) {
- super(in_bytes);
+ public AltosTelemetryRecordSensor(int[] in_bytes, int rssi) {
+ super(in_bytes, rssi);
state = uint8(5);
accel = int16(6);
ground_accel = int16(26);
accel_plus_g = int16(28);
accel_minus_g = int16(30);
-
- rssi = in_rssi;
}
public AltosRecord update_state(AltosRecord prev) {
next.accel_minus_g = AltosRecord.MISSING;
}
- next.rssi = rssi;
-
next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt;
return next;
AltosIMUQuery.java \
AltosLine.java \
AltosLink.java \
+ AltosListenerState.java \
AltosLog.java \
AltosMs5607.java \
AltosMs5607Query.java \
label.setVisible(false);
}
- void show(AltosState state, int crc_errors) {}
+ void show(AltosState state, AltosListenerState listener_state) {}
void show(String s) {
show();
public class AscentValue {
JLabel label;
JTextField value;
- void show(AltosState state, int crc_errors) {}
+ void show(AltosState state, AltosListenerState listener_state) {}
void reset() {
value.setText("");
JTextField max_value;
double max;
- void show(AltosState state, int crc_errors) {}
+ void show(AltosState state, AltosListenerState listener_state) {}
void reset() {
value.setText("");
class Height extends AscentValueHold {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show(AltosConvert.height, state.height);
}
public Height (GridBagLayout layout, int y) {
Height height;
class Speed extends AscentValueHold {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
double speed = state.accel_speed;
if (!state.ascent)
speed = state.baro_speed;
Speed speed;
class Accel extends AscentValueHold {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show(AltosConvert.accel, state.acceleration);
}
public Accel (GridBagLayout layout, int y) {
}
class Apogee extends AscentStatus {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show("%4.2f V", state.drogue_sense);
lights.set(state.drogue_sense > 3.2);
}
Apogee apogee;
class Main extends AscentStatus {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show("%4.2f V", state.main_sense);
lights.set(state.main_sense > 3.2);
}
Main main;
class Lat extends AscentValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
if (state.gps != null)
show(pos(state.gps.lat,"N", "S"));
else
Lat lat;
class Lon extends AscentValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
if (state.gps != null)
show(pos(state.gps.lon,"E", "W"));
else
accel.set_font();
}
- public void show(AltosState state, int crc_errors) {
+ public void show(AltosState state, AltosListenerState listener_state) {
if (state.gps != null && state.gps.connected) {
- lat.show(state, crc_errors);
- lon.show(state, crc_errors);
+ lat.show(state, listener_state);
+ lon.show(state, listener_state);
} else {
lat.hide();
lon.hide();
}
- height.show(state, crc_errors);
+ height.show(state, listener_state);
if (state.main_sense != AltosRecord.MISSING)
- main.show(state, crc_errors);
+ main.show(state, listener_state);
else
main.hide();
if (state.drogue_sense != AltosRecord.MISSING)
- apogee.show(state, crc_errors);
+ apogee.show(state, listener_state);
else
apogee.hide();
- speed.show(state, crc_errors);
- accel.show(state, crc_errors);
+ speed.show(state, listener_state);
+ accel.show(state, listener_state);
}
public void labels(GridBagLayout layout, int y) {
add(max);
}
+ public String getName() {
+ return "Ascent";
+ }
+
public AltosAscent() {
layout = new GridBagLayout();
}
}
- public void show(AltosState state, int crc_errors) {
+ public void show(AltosState state, AltosListenerState listener_state) {
if (state == null)
return;
if (state.data.companion != null)
public void set_main_deploy(int new_main_deploy) {
main_deploy_value.setSelectedItem(Integer.toString(new_main_deploy));
+ main_deploy_value.setEnabled(new_main_deploy >= 0);
}
public int main_deploy() {
"eeprom"));
setFileFilter(new FileNameExtensionFilter("Telemetry file",
"telem"));
- setFileFilter(new FileNameExtensionFilter("MegaMetrum eeprom file",
+ setFileFilter(new FileNameExtensionFilter("TeleMega eeprom file",
"mega"));
setFileFilter(new FileNameExtensionFilter("Flight data file",
"telem", "eeprom", "mega"));
JTextField value;
AltosLights lights;
- abstract void show(AltosState state, int crc_errors);
+ abstract void show(AltosState state, AltosListenerState listener_state);
void show() {
label.setVisible(true);
value.setText("");
}
- abstract void show(AltosState state, int crc_errors);
+ abstract void show(AltosState state, AltosListenerState listener_state);
void show() {
label.setVisible(true);
value2.setFont(Altos.value_font);
}
- abstract void show(AltosState state, int crc_errors);
+ abstract void show(AltosState state, AltosListenerState listener_state);
void show(String v1, String v2) {
show();
}
class Height extends DescentValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show(AltosConvert.height, state.height);
}
public Height (GridBagLayout layout, int x, int y) {
Height height;
class Speed extends DescentValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
double speed = state.accel_speed;
if (!state.ascent)
speed = state.baro_speed;
}
class Lat extends DescentValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
if (state.gps != null && state.gps.connected)
show(pos(state.gps.lat,"N", "S"));
else
Lat lat;
class Lon extends DescentValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
if (state.gps != null && state.gps.connected)
show(pos(state.gps.lon,"W", "E"));
else
Lon lon;
class Distance extends DescentValue {
- void show(AltosState state, int crc_errors) {
+ void show(AltosState state, AltosListenerState listener_state) {
if (state.from_pad != null)
show(AltosConvert.distance, state.from_pad.distance);
else
class Apogee extends DescentStatus {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show("%4.2f V", state.drogue_sense);
lights.set(state.drogue_sense > 3.2);
}
Apogee apogee;
class Main extends DescentStatus {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show("%4.2f V", state.main_sense);
lights.set(state.main_sense > 3.2);
}
Main main;
class Bearing extends DescentDualValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
if (state.from_pad != null) {
show( String.format("%3.0f°", state.from_pad.bearing),
state.from_pad.bearing_words(
Bearing bearing;
class Range extends DescentValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show(AltosConvert.distance, state.range);
}
public Range (GridBagLayout layout, int x, int y) {
Range range;
class Elevation extends DescentValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show("%3.0f°", state.elevation);
}
public Elevation (GridBagLayout layout, int x, int y) {
apogee.set_font();
}
- public void show(AltosState state, int crc_errors) {
- height.show(state, crc_errors);
- speed.show(state, crc_errors);
+ public void show(AltosState state, AltosListenerState listener_state) {
+ height.show(state, listener_state);
+ speed.show(state, listener_state);
if (state.gps != null && state.gps.connected) {
- bearing.show(state, crc_errors);
- range.show(state, crc_errors);
- distance.show(state, crc_errors);
- elevation.show(state, crc_errors);
- lat.show(state, crc_errors);
- lon.show(state, crc_errors);
+ bearing.show(state, listener_state);
+ range.show(state, listener_state);
+ distance.show(state, listener_state);
+ elevation.show(state, listener_state);
+ lat.show(state, listener_state);
+ lon.show(state, listener_state);
} else {
bearing.hide();
range.hide();
lon.hide();
}
if (state.main_sense != AltosRecord.MISSING)
- main.show(state, crc_errors);
+ main.show(state, listener_state);
else
main.hide();
if (state.drogue_sense != AltosRecord.MISSING)
- apogee.show(state, crc_errors);
+ apogee.show(state, listener_state);
else
apogee.hide();
}
+ public String getName() {
+ return "Descent";
+ }
+
public AltosDescent() {
layout = new GridBagLayout();
IdleThread idle_thread;
AltosVoice voice;
AltosFlightReader reader;
- int crc_errors;
+ AltosState old_state, state;
+ AltosListenerState listener_state;
AltosFlightDisplay display;
- void show_internal(AltosState state, int crc_errors) {
- if (state != null)
- display.show(state, crc_errors);
- }
-
- void show_safely(AltosState in_state, int in_crc_errors) {
- final AltosState state = in_state;
- final int crc_errors = in_crc_errors;
+ synchronized void show_safely() {
+ final AltosState my_state = state;
+ final AltosListenerState my_listener_state = listener_state;
Runnable r = new Runnable() {
public void run() {
try {
- show_internal(state, crc_errors);
+ display.show(my_state, my_listener_state);
} catch (Exception ex) {
}
}
class IdleThread extends Thread {
boolean started;
- private AltosState state;
int reported_landing;
int report_interval;
long report_time;
++reported_landing;
if (state.state != Altos.ao_flight_landed) {
state.state = Altos.ao_flight_landed;
- show_safely(state, 0);
+ show_safely();
}
}
}
public void run () {
try {
for (;;) {
+ if (reader.has_monitor_battery()) {
+ listener_state.battery = reader.monitor_battery();
+ show_safely();
+ }
set_report_time();
for (;;) {
voice.drain();
wait(sleep_time);
}
}
+
report(false);
}
} catch (InterruptedException ie) {
}
}
- public synchronized void notice(AltosState new_state, boolean spoken) {
- AltosState old_state = state;
- state = new_state;
- if (!started && state.state > Altos.ao_flight_pad) {
- started = true;
- start();
- }
-
- if (state.state < Altos.ao_flight_drogue)
- report_interval = 10000;
- else
- report_interval = 20000;
+ public synchronized void notice(boolean spoken) {
if (old_state != null && old_state.state != state.state) {
report_time = now();
this.notify();
}
public IdleThread() {
- state = null;
reported_landing = 0;
report_interval = 10000;
}
}
- boolean tell(AltosState state, AltosState old_state) {
+ synchronized boolean tell() {
boolean ret = false;
if (old_state == null || old_state.state != state.state) {
voice.speak(state.data.state());
public void run() {
boolean interrupted = false;
- //String line;
- AltosState state = null;
- AltosState old_state = null;
boolean told;
idle_thread = new IdleThread();
+ idle_thread.start();
try {
for (;;) {
old_state = state;
state = new AltosState(record, state);
reader.update(state);
- show_safely(state, crc_errors);
- told = tell(state, old_state);
- idle_thread.notice(state, told);
+ show_safely();
+ told = tell();
+ idle_thread.notice(told);
} catch (ParseException pp) {
System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage());
} catch (AltosCRCException ce) {
- ++crc_errors;
- show_safely(state, crc_errors);
+ ++listener_state.crc_errors;
+ show_safely();
}
}
} catch (InterruptedException ee) {
}
public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) {
+ listener_state = new AltosListenerState();
parent = in_parent;
voice = in_voice;
display = in_display;
extension = "science";
CaptureTeleScience(eechunk);
break;
- case AltosLib.AO_LOG_FORMAT_MEGAMETRUM:
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA:
extension = "mega";
CaptureMega(eechunk);
}
public interface AltosFlightDisplay {
void reset();
- void show(AltosState state, int crc_errors);
+ void show(AltosState state, AltosListenerState listener_state);
void set_font();
}
max_speed = state.max_baro_speed;
max_acceleration = state.max_acceleration;
}
- if (state.gps.locked && state.gps.nsat >= 4) {
+ if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
if (state.state <= Altos.ao_flight_pad) {
pad_lat = state.gps.lat;
pad_lon = state.gps.lon;
JLabel label;
JTextField value;
- void show(AltosState state, int crc_errors) {}
+ void show(AltosState state, AltosListenerState listener_state) {}
void reset() {
value.setText("");
}
class Call extends FlightValue {
- void show(AltosState state, int crc_errors) {
+ void show(AltosState state, AltosListenerState listener_state) {
value.setText(state.data.callsign);
}
public Call (GridBagLayout layout, int x) {
Call call;
class Serial extends FlightValue {
- void show(AltosState state, int crc_errors) {
- value.setText(String.format("%d", state.data.serial));
+ void show(AltosState state, AltosListenerState listener_state) {
+ if (state.data.serial == AltosRecord.MISSING)
+ value.setText("none");
+ else
+ value.setText(String.format("%d", state.data.serial));
}
public Serial (GridBagLayout layout, int x) {
super (layout, x, "Serial");
Serial serial;
class Flight extends FlightValue {
- void show(AltosState state, int crc_errors) {
- value.setText(String.format("%d", state.data.flight));
+ void show(AltosState state, AltosListenerState listener_state) {
+ if (state.data.flight == AltosRecord.MISSING)
+ value.setText("none");
+ else
+ value.setText(String.format("%d", state.data.flight));
}
public Flight (GridBagLayout layout, int x) {
super (layout, x, "Flight");
Flight flight;
class FlightState extends FlightValue {
- void show(AltosState state, int crc_errors) {
+ void show(AltosState state, AltosListenerState listener_state) {
value.setText(state.data.state());
}
public FlightState (GridBagLayout layout, int x) {
FlightState flight_state;
class RSSI extends FlightValue {
- void show(AltosState state, int crc_errors) {
+ void show(AltosState state, AltosListenerState listener_state) {
value.setText(String.format("%d", state.data.rssi));
}
public RSSI (GridBagLayout layout, int x) {
RSSI rssi;
class LastPacket extends FlightValue {
- void show(AltosState state, int crc_errors) {
+ void show(AltosState state, AltosListenerState listener_state) {
long secs = (System.currentTimeMillis() - state.report_time + 500) / 1000;
value.setText(String.format("%d", secs));
}
last_packet.set_font();
}
- public void show (AltosState state, int crc_errors) {
- call.show(state, crc_errors);
- serial.show(state, crc_errors);
- flight.show(state, crc_errors);
- flight_state.show(state, crc_errors);
- rssi.show(state, crc_errors);
- last_packet.show(state, crc_errors);
+ public void show (AltosState state, AltosListenerState listener_state) {
+ call.show(state, listener_state);
+ serial.show(state, listener_state);
+ flight.show(state, listener_state);
+ flight_state.show(state, listener_state);
+ rssi.show(state, listener_state);
+ last_packet.show(state, listener_state);
}
public int height() {
public class AltosFlightStatusUpdate implements ActionListener {
- public AltosState saved_state;
- AltosFlightStatus flightStatus;
+ public AltosState saved_state;
+ public AltosListenerState saved_listener_state;
+ AltosFlightStatus flightStatus;
public void actionPerformed (ActionEvent e) {
- if (saved_state != null)
- flightStatus.show(saved_state, 0);
+ if (saved_state != null) {
+ if (saved_listener_state == null)
+ saved_listener_state = new AltosListenerState();
+ flightStatus.show(saved_state, saved_listener_state);
+ }
}
public AltosFlightStatusUpdate (AltosFlightStatus in_flightStatus) {
AltosSiteMap sitemap;
boolean has_map;
boolean has_companion;
+ boolean has_state;
private AltosFlightStatus flightStatus;
private AltosInfoTable flightInfo;
AltosFlightStatusUpdate status_update;
- public void show(AltosState state, int crc_errors) {
+ public void show(AltosState state, AltosListenerState listener_state) {
status_update.saved_state = state;
+
+ if (state == null)
+ state = new AltosState(new AltosRecord());
+
+ pad.show(state, listener_state);
+
+ if (state.state != Altos.ao_flight_startup) {
+ if (!has_state) {
+ pane.setTitleAt(0, "Launch Pad");
+ pane.add(ascent, 1);
+ pane.add(descent, 2);
+ pane.add(landed, 3);
+ has_state = true;
+ }
+ }
+
+ ascent.show(state, listener_state);
+ descent.show(state, listener_state);
+ landed.show(state, listener_state);
+
JComponent tab = which_tab(state);
- try {
- pad.show(state, crc_errors);
- ascent.show(state, crc_errors);
- descent.show(state, crc_errors);
- landed.show(state, crc_errors);
if (tab != cur_tab) {
if (cur_tab == pane.getSelectedComponent()) {
pane.setSelectedComponent(tab);
}
cur_tab = tab;
}
- flightStatus.show(state, crc_errors);
- flightInfo.show(state, crc_errors);
+ flightStatus.show(state, listener_state);
+ flightInfo.show(state, listener_state);
if (state.data.companion != null) {
if (!has_companion) {
pane.add("Companion", companion);
has_companion= true;
}
- companion.show(state, crc_errors);
+ companion.show(state, listener_state);
} else {
if (has_companion) {
pane.remove(companion);
pane.add("Site Map", sitemap);
has_map = true;
}
- sitemap.show(state, crc_errors);
+ sitemap.show(state, listener_state);
} else {
if (has_map) {
pane.remove(sitemap);
has_map = false;
}
}
- } catch (Exception e) {
- System.out.print("Show exception " + e + "\n");
- e.printStackTrace();
- }
}
public void set_exit_on_close() {
pane = new JTabbedPane();
pad = new AltosPad();
- pane.add("Launch Pad", pad);
+ pane.add("Status", pad);
ascent = new AltosAscent();
- pane.add("Ascent", ascent);
-
descent = new AltosDescent();
- pane.add("Descent", descent);
-
landed = new AltosLanded(reader);
- pane.add("Landed", landed);
flightInfo = new AltosInfoTable();
pane.add("Table", new JScrollPane(flightInfo));
companion = new AltosCompanionInfo();
has_companion = false;
+ has_state = false;
sitemap = new AltosSiteMap();
has_map = false;
AltosGraphDataSet graphDataSet;
AltosFlightStats stats;
AltosFlightStatsTable statsTable;
+ boolean has_gps;
- boolean fill_map(AltosRecordIterable records) {
+ void fill_map(AltosRecordIterable records) {
boolean any_gps = false;
for (AltosRecord record : records) {
state = new AltosState(record, state);
- if (state.gps.locked && state.gps.nsat >= 4) {
- map.show(state, 0);
- any_gps = true;
+ if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
+ if (map == null)
+ map = new AltosSiteMap();
+ map.show(state, null);
+ has_gps = true;
}
}
- return any_gps;
}
AltosGraphUI(AltosRecordIterable records, File file) throws InterruptedException, IOException {
statsTable = new AltosFlightStatsTable(stats);
- map = new AltosSiteMap();
-
pane.add("Flight Graph", graph.panel);
pane.add("Configure Graph", enable);
pane.add("Flight Statistics", statsTable);
- if (fill_map(records))
+ has_gps = false;
+ fill_map(records);
+ if (has_gps)
pane.add("Map", map);
setContentPane (pane);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setVisible(true);
- if (state != null)
+ if (state != null && has_gps)
map.centre(state);
}
}
AltosFlightStatusUpdate status_update;
- public void show(AltosState state, int crc_errors) {
+ public void show(AltosState state, AltosListenerState listener_state) {
status_update.saved_state = state;
try {
- pad.show(state, crc_errors);
- flightStatus.show(state, crc_errors);
- flightInfo.show(state, crc_errors);
+ pad.show(state, listener_state);
+ flightStatus.show(state, listener_state);
+ flightInfo.show(state, listener_state);
} catch (Exception e) {
System.out.print("Show exception" + e);
}
}
- public void update(final AltosState state) {
+ public void update(final AltosState state, final AltosListenerState listener_state) {
Runnable r = new Runnable() {
public void run() {
- show(state, 0);
+ show(state, listener_state);
}
};
SwingUtilities.invokeLater(r);
model.clear();
}
- public void show(AltosState state, int crc_errors) {
- if (state == null)
- return;
+ public void show(AltosState state, AltosListenerState listener_state) {
info_reset();
- info_add_row(0, "Altitude", "%6.0f m", state.altitude);
- info_add_row(0, "Pad altitude", "%6.0f m", state.ground_altitude);
- info_add_row(0, "Height", "%6.0f m", state.height);
- info_add_row(0, "Max height", "%6.0f m", state.max_height);
- info_add_row(0, "Acceleration", "%8.1f m/s²", state.acceleration);
- info_add_row(0, "Max acceleration", "%8.1f m/s²", state.max_acceleration);
- info_add_row(0, "Speed", "%8.1f m/s", state.speed());
- info_add_row(0, "Max Speed", "%8.1f m/s", state.max_accel_speed);
- info_add_row(0, "Temperature", "%9.2f °C", state.temperature);
- info_add_row(0, "Battery", "%9.2f V", state.battery);
- if (state.drogue_sense != AltosRecord.MISSING)
- info_add_row(0, "Drogue", "%9.2f V", state.drogue_sense);
- if (state.main_sense != AltosRecord.MISSING)
- info_add_row(0, "Main", "%9.2f V", state.main_sense);
- info_add_row(0, "CRC Errors", "%6d", crc_errors);
-
- if (state.gps == null || !state.gps.connected) {
- info_add_row(1, "GPS", "not available");
- } else {
- if (state.gps_ready)
- info_add_row(1, "GPS state", "%s", "ready");
- else
- info_add_row(1, "GPS state", "wait (%d)",
- state.gps_waiting);
- if (state.data.gps.locked)
- info_add_row(1, "GPS", " locked");
- else if (state.data.gps.connected)
- info_add_row(1, "GPS", " unlocked");
- else
- info_add_row(1, "GPS", " missing");
- info_add_row(1, "Satellites", "%6d", state.data.gps.nsat);
- info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S');
- info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W');
- info_add_row(1, "GPS altitude", "%6d", state.gps.alt);
- info_add_row(1, "GPS height", "%6.0f", state.gps_height);
-
- /* The SkyTraq GPS doesn't report these values */
- /*
- if (false) {
- info_add_row(1, "GPS ground speed", "%8.1f m/s %3d°",
- state.gps.ground_speed,
- state.gps.course);
- info_add_row(1, "GPS climb rate", "%8.1f m/s",
- state.gps.climb_rate);
- info_add_row(1, "GPS error", "%6d m(h)%3d m(v)",
- state.gps.h_error, state.gps.v_error);
- }
- */
-
- info_add_row(1, "GPS hdop", "%8.1f", state.gps.hdop);
-
- if (state.npad > 0) {
- if (state.from_pad != null) {
- info_add_row(1, "Distance from pad", "%6d m",
- (int) (state.from_pad.distance + 0.5));
- info_add_row(1, "Direction from pad", "%6d°",
- (int) (state.from_pad.bearing + 0.5));
- info_add_row(1, "Elevation from pad", "%6d°",
- (int) (state.elevation + 0.5));
- info_add_row(1, "Range from pad", "%6d m",
- (int) (state.range + 0.5));
- } else {
- info_add_row(1, "Distance from pad", "unknown");
- info_add_row(1, "Direction from pad", "unknown");
- info_add_row(1, "Elevation from pad", "unknown");
- info_add_row(1, "Range from pad", "unknown");
+ if (state != null) {
+ if (state.altitude != AltosRecord.MISSING)
+ info_add_row(0, "Altitude", "%6.0f m", state.altitude);
+ if (state.ground_altitude != AltosRecord.MISSING)
+ info_add_row(0, "Pad altitude", "%6.0f m", state.ground_altitude);
+ if (state.height != AltosRecord.MISSING)
+ info_add_row(0, "Height", "%6.0f m", state.height);
+ if (state.height != AltosRecord.MISSING)
+ info_add_row(0, "Max height", "%6.0f m", state.max_height);
+ if (state.acceleration != AltosRecord.MISSING)
+ info_add_row(0, "Acceleration", "%8.1f m/s²", state.acceleration);
+ if (state.acceleration != AltosRecord.MISSING)
+ info_add_row(0, "Max acceleration", "%8.1f m/s²", state.max_acceleration);
+ if (state.speed() != AltosRecord.MISSING)
+ info_add_row(0, "Speed", "%8.1f m/s", state.speed());
+ if (state.speed() != AltosRecord.MISSING)
+ info_add_row(0, "Max Speed", "%8.1f m/s", state.max_accel_speed);
+ if (state.temperature != AltosRecord.MISSING)
+ info_add_row(0, "Temperature", "%9.2f °C", state.temperature);
+ if (state.battery != AltosRecord.MISSING)
+ info_add_row(0, "Battery", "%9.2f V", state.battery);
+ if (state.drogue_sense != AltosRecord.MISSING)
+ info_add_row(0, "Drogue", "%9.2f V", state.drogue_sense);
+ if (state.main_sense != AltosRecord.MISSING)
+ info_add_row(0, "Main", "%9.2f V", state.main_sense);
+ }
+ if (listener_state != null) {
+ info_add_row(0, "CRC Errors", "%6d", listener_state.crc_errors);
+
+ if (listener_state.battery != AltosRecord.MISSING)
+ info_add_row(0, "Receiver Battery", "%9.2f", listener_state.battery);
+ }
+
+ if (state != null) {
+ if (state.gps == null || !state.gps.connected) {
+ info_add_row(1, "GPS", "not available");
+ } else {
+ if (state.gps_ready)
+ info_add_row(1, "GPS state", "%s", "ready");
+ else
+ info_add_row(1, "GPS state", "wait (%d)",
+ state.gps_waiting);
+ if (state.data.gps.locked)
+ info_add_row(1, "GPS", " locked");
+ else if (state.data.gps.connected)
+ info_add_row(1, "GPS", " unlocked");
+ else
+ info_add_row(1, "GPS", " missing");
+ info_add_row(1, "Satellites", "%6d", state.data.gps.nsat);
+ info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S');
+ info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W');
+ info_add_row(1, "GPS altitude", "%6d", state.gps.alt);
+ info_add_row(1, "GPS height", "%6.0f", state.gps_height);
+
+ /* The SkyTraq GPS doesn't report these values */
+ /*
+ if (false) {
+ info_add_row(1, "GPS ground speed", "%8.1f m/s %3d°",
+ state.gps.ground_speed,
+ state.gps.course);
+ info_add_row(1, "GPS climb rate", "%8.1f m/s",
+ state.gps.climb_rate);
+ info_add_row(1, "GPS error", "%6d m(h)%3d m(v)",
+ state.gps.h_error, state.gps.v_error);
+ }
+ */
+
+ info_add_row(1, "GPS hdop", "%8.1f", state.gps.hdop);
+
+ if (state.npad > 0) {
+ if (state.from_pad != null) {
+ info_add_row(1, "Distance from pad", "%6d m",
+ (int) (state.from_pad.distance + 0.5));
+ info_add_row(1, "Direction from pad", "%6d°",
+ (int) (state.from_pad.bearing + 0.5));
+ info_add_row(1, "Elevation from pad", "%6d°",
+ (int) (state.elevation + 0.5));
+ info_add_row(1, "Range from pad", "%6d m",
+ (int) (state.range + 0.5));
+ } else {
+ info_add_row(1, "Distance from pad", "unknown");
+ info_add_row(1, "Direction from pad", "unknown");
+ info_add_row(1, "Elevation from pad", "unknown");
+ info_add_row(1, "Range from pad", "unknown");
+ }
+ info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S');
+ info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W');
+ info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt);
}
- info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S');
- info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W');
- info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt);
- }
- info_add_row(1, "GPS date", "%04d-%02d-%02d",
- state.gps.year,
- state.gps.month,
- state.gps.day);
- info_add_row(1, "GPS time", " %02d:%02d:%02d",
- state.gps.hour,
- state.gps.minute,
- state.gps.second);
- //int nsat_vis = 0;
- int c;
-
- if (state.gps.cc_gps_sat == null)
- info_add_row(2, "Satellites Visible", "%4d", 0);
- else {
- info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length);
- for (c = 0; c < state.gps.cc_gps_sat.length; c++) {
- info_add_row(2, "Satellite id,C/N0",
- "%4d, %4d",
- state.gps.cc_gps_sat[c].svid,
- state.gps.cc_gps_sat[c].c_n0);
+ info_add_row(1, "GPS date", "%04d-%02d-%02d",
+ state.gps.year,
+ state.gps.month,
+ state.gps.day);
+ info_add_row(1, "GPS time", " %02d:%02d:%02d",
+ state.gps.hour,
+ state.gps.minute,
+ state.gps.second);
+ //int nsat_vis = 0;
+ int c;
+
+ if (state.gps.cc_gps_sat == null)
+ info_add_row(2, "Satellites Visible", "%4d", 0);
+ else {
+ info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length);
+ for (c = 0; c < state.gps.cc_gps_sat.length; c++) {
+ info_add_row(2, "Satellite id,C/N0",
+ "%4d, %4d",
+ state.gps.cc_gps_sat[c].svid,
+ state.gps.cc_gps_sat[c].c_n0);
+ }
}
}
}
public class LandedValue {
JLabel label;
JTextField value;
- void show(AltosState state, int crc_errors) {}
+ void show(AltosState state, AltosListenerState listener_state) {}
void reset() {
value.setText("");
}
class Lat extends LandedValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
if (state.gps != null && state.gps.connected)
show(pos(state.gps.lat,"N", "S"));
else
Lat lat;
class Lon extends LandedValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show();
if (state.gps != null && state.gps.connected)
show(pos(state.gps.lon,"E", "W"));
Lon lon;
class Bearing extends LandedValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show();
if (state.from_pad != null)
show("%3.0f°", state.from_pad.bearing);
Bearing bearing;
class Distance extends LandedValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show();
if (state.from_pad != null)
show(AltosConvert.distance, state.from_pad.distance);
Distance distance;
class Height extends LandedValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show(AltosConvert.height, state.max_height);
}
public Height (GridBagLayout layout, int y) {
Height height;
class Speed extends LandedValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show(AltosConvert.speed, state.max_speed());
}
public Speed (GridBagLayout layout, int y) {
Speed speed;
class Accel extends LandedValue {
- void show (AltosState state, int crc_errors) {
+ void show (AltosState state, AltosListenerState listener_state) {
show(AltosConvert.accel, state.max_acceleration);
}
public Accel (GridBagLayout layout, int y) {
accel.set_font();
}
- public void show(AltosState state, int crc_errors) {
+ public void show(AltosState state, AltosListenerState listener_state) {
if (state.gps != null && state.gps.connected) {
- bearing.show(state, crc_errors);
- distance.show(state, crc_errors);
- lat.show(state, crc_errors);
- lon.show(state, crc_errors);
+ bearing.show(state, listener_state);
+ distance.show(state, listener_state);
+ lat.show(state, listener_state);
+ lon.show(state, listener_state);
} else {
bearing.hide();
distance.hide();
lat.hide();
lon.hide();
}
- height.show(state, crc_errors);
- speed.show(state, crc_errors);
- accel.show(state, crc_errors);
+ height.show(state, listener_state);
+ speed.show(state, listener_state);
+ accel.show(state, listener_state);
if (reader.backing_file() != null)
graph.setEnabled(true);
}
}
}
+ public String getName() {
+ return "Landed";
+ }
+
public AltosLanded(AltosFlightReader in_reader) {
layout = new GridBagLayout();
JTextField value;
AltosLights lights;
- void show(AltosState state, int crc_errors) {}
+ void show(AltosState state, AltosListenerState listener_state) {}
void reset() {
value.setText("");
value.setFont(Altos.value_font);
}
+ public void set_label(String text) {
+ label.setText(text);
+ }
+
public LaunchStatus (GridBagLayout layout, int y, String text) {
GridBagConstraints c = new GridBagConstraints();
c.weighty = 1;
public class LaunchValue {
JLabel label;
JTextField value;
- void show(AltosState state, int crc_errors) {}
+ void show(AltosState state, AltosListenerState listener_state) {}
void show() {
label.setVisible(true);
show(String.format(format, v));
}
+ public void set_label(String text) {
+ label.setText(text);
+ }
+
void reset() {
value.setText("");
}
}
class Battery extends LaunchStatus {
- void show (AltosState state, int crc_errors) {
- show("%4.2f V", state.battery);
- lights.set(state.battery > 3.7);
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null || state.battery == AltosRecord.MISSING)
+ hide();
+ else {
+ show("%4.2f V", state.battery);
+ lights.set(state.battery > 3.7);
+ }
}
public Battery (GridBagLayout layout, int y) {
super(layout, y, "Battery Voltage");
Battery battery;
class Apogee extends LaunchStatus {
- void show (AltosState state, int crc_errors) {
- show("%4.2f V", state.drogue_sense);
- lights.set(state.drogue_sense > 3.2);
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null || state.drogue_sense == AltosRecord.MISSING)
+ hide();
+ else {
+ show("%4.2f V", state.drogue_sense);
+ lights.set(state.drogue_sense > 3.2);
+ }
}
public Apogee (GridBagLayout layout, int y) {
super(layout, y, "Apogee Igniter Voltage");
Apogee apogee;
class Main extends LaunchStatus {
- void show (AltosState state, int crc_errors) {
- show("%4.2f V", state.main_sense);
- lights.set(state.main_sense > 3.2);
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null || state.main_sense == AltosRecord.MISSING)
+ hide();
+ else {
+ show("%4.2f V", state.main_sense);
+ lights.set(state.main_sense > 3.2);
+ }
}
public Main (GridBagLayout layout, int y) {
super(layout, y, "Main Igniter Voltage");
Main main;
class LoggingReady extends LaunchStatus {
- void show (AltosState state, int crc_errors) {
- if (state.data.flight != 0) {
- if (state.data.state <= Altos.ao_flight_pad)
- show("Ready to record");
- else if (state.data.state < Altos.ao_flight_landed)
- show("Recording data");
- else
- show("Recorded data");
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null || state.data.flight == AltosRecord.MISSING) {
+ hide();
+ } else {
+ if (state.data.flight != 0) {
+ if (state.data.state <= Altos.ao_flight_pad)
+ show("Ready to record");
+ else if (state.data.state < Altos.ao_flight_landed)
+ show("Recording data");
+ else
+ show("Recorded data");
+ } else
+ show("Storage full");
+ lights.set(state.data.flight != 0);
}
- else
- show("Storage full");
- lights.set(state.data.flight != 0);
}
public LoggingReady (GridBagLayout layout, int y) {
super(layout, y, "On-board Data Logging");
LoggingReady logging_ready;
class GPSLocked extends LaunchStatus {
- void show (AltosState state, int crc_errors) {
- show("%4d sats", state.gps.nsat);
- lights.set(state.gps.locked && state.gps.nsat >= 4);
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null || state.gps == null)
+ hide();
+ else {
+ show("%4d sats", state.gps.nsat);
+ lights.set(state.gps.locked && state.gps.nsat >= 4);
+ }
}
public GPSLocked (GridBagLayout layout, int y) {
super (layout, y, "GPS Locked");
GPSLocked gps_locked;
class GPSReady extends LaunchStatus {
- void show (AltosState state, int crc_errors) {
- if (state.gps_ready)
- show("Ready");
- else
- show("Waiting %d", state.gps_waiting);
- lights.set(state.gps_ready);
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null || state.gps == null)
+ hide();
+ else {
+ if (state.gps_ready)
+ show("Ready");
+ else
+ show("Waiting %d", state.gps_waiting);
+ lights.set(state.gps_ready);
+ }
}
public GPSReady (GridBagLayout layout, int y) {
super (layout, y, "GPS Ready");
GPSReady gps_ready;
+ class ReceiverBattery extends LaunchStatus {
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (listener_state == null || listener_state.battery == AltosRecord.MISSING)
+ hide();
+ else {
+ show("%4.2f V", listener_state.battery);
+ lights.set(listener_state.battery > 3.7);
+ }
+ }
+ public ReceiverBattery (GridBagLayout layout, int y) {
+ super(layout, y, "Receiver Battery");
+ }
+ }
+
+ ReceiverBattery receiver_battery;
+
String pos(double p, String pos, String neg) {
String h = pos;
if (p < 0) {
}
class PadLat extends LaunchValue {
- void show (AltosState state, int crc_errors) {
- show(pos(state.pad_lat,"N", "S"));
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null || state.gps == null) {
+ hide();
+ } else {
+ if (state.state < AltosLib.ao_flight_pad) {
+ show(pos(state.gps.lat,"N", "S"));
+ set_label("Latitude");
+ } else {
+ show(pos(state.pad_lat,"N", "S"));
+ set_label("Pad Latitude");
+ }
+ }
}
public PadLat (GridBagLayout layout, int y) {
super (layout, y, "Pad Latitude");
PadLat pad_lat;
class PadLon extends LaunchValue {
- void show (AltosState state, int crc_errors) {
- show(pos(state.pad_lon,"E", "W"));
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null || state.gps == null) {
+ hide();
+ } else {
+ if (state.state < AltosLib.ao_flight_pad) {
+ show(pos(state.gps.lon,"E", "W"));
+ set_label("Longitude");
+ } else {
+ show(pos(state.pad_lon,"E", "W"));
+ set_label("Pad Longitude");
+ }
+ }
}
public PadLon (GridBagLayout layout, int y) {
super (layout, y, "Pad Longitude");
PadLon pad_lon;
class PadAlt extends LaunchValue {
- void show (AltosState state, int crc_errors) {
- show("%4.0f m", state.pad_alt);
+ void show (AltosState state, AltosListenerState listener_state) {
+ if (state == null)
+ hide();
+ else {
+ if (state.state < AltosLib.ao_flight_pad && state.gps != null) {
+ show("%4.0f m", state.gps.alt);
+ set_label("Altitude");
+ } else {
+ if (state.pad_alt == AltosRecord.MISSING)
+ hide();
+ else {
+ show("%4.0f m", state.pad_alt);
+ set_label("Pad Altitude");
+ }
+ }
+ }
}
public PadAlt (GridBagLayout layout, int y) {
super (layout, y, "Pad Altitude");
logging_ready.reset();
gps_locked.reset();
gps_ready.reset();
+ receiver_battery.reset();
pad_lat.reset();
pad_lon.reset();
pad_alt.reset();
logging_ready.set_font();
gps_locked.set_font();
gps_ready.set_font();
+ receiver_battery.set_font();
pad_lat.set_font();
pad_lon.set_font();
pad_alt.set_font();
}
- public void show(AltosState state, int crc_errors) {
- battery.show(state, crc_errors);
- if (state.drogue_sense == AltosRecord.MISSING)
- apogee.hide();
- else
- apogee.show(state, crc_errors);
- if (state.main_sense == AltosRecord.MISSING)
- main.hide();
- else
- main.show(state, crc_errors);
- logging_ready.show(state, crc_errors);
- pad_alt.show(state, crc_errors);
- if (state.gps != null && state.gps.connected) {
- gps_locked.show(state, crc_errors);
- gps_ready.show(state, crc_errors);
- pad_lat.show(state, crc_errors);
- pad_lon.show(state, crc_errors);
- } else {
- gps_locked.hide();
- gps_ready.hide();
- pad_lat.hide();
- pad_lon.hide();
- }
+ public void show(AltosState state, AltosListenerState listener_state) {
+ battery.show(state, listener_state);
+ apogee.show(state, listener_state);
+ main.show(state, listener_state);
+ logging_ready.show(state, listener_state);
+ pad_alt.show(state, listener_state);
+ receiver_battery.show(state, listener_state);
+ gps_locked.show(state, listener_state);
+ gps_ready.show(state, listener_state);
+ pad_lat.show(state, listener_state);
+ pad_lon.show(state, listener_state);
}
public AltosPad() {
logging_ready = new LoggingReady(layout, 3);
gps_locked = new GPSLocked(layout, 4);
gps_ready = new GPSReady(layout, 5);
- pad_lat = new PadLat(layout, 6);
- pad_lon = new PadLon(layout, 7);
- pad_alt = new PadAlt(layout, 8);
+ receiver_battery = new ReceiverBattery(layout, 6);
+ pad_lat = new PadLat(layout, 7);
+ pad_lon = new PadLon(layout, 8);
+ pad_alt = new PadAlt(layout, 9);
+ show(null, null);
}
}
return pngfile.toString();
}
+ public void initAndFinishMapAsync (final AltosSiteMapTile tile, final Point offset) {
+ Thread thread = new Thread() {
+ public void run() {
+ initMap(offset);
+ finishTileLater(tile, offset);
+ }
+ };
+ thread.start();
+ }
+
public void setBaseLocation(double lat, double lng) {
for (Point k : mapTiles.keySet()) {
AltosSiteMapTile tile = mapTiles.get(k);
initMaps(lat, lon);
scrollRocketToVisible(pt(lat, lon));
}
- public void show(final AltosState state, final int crc_errors) {
+ public void show(final AltosState state, final AltosListenerState listener_state) {
// if insufficient gps data, nothing to update
if (!state.gps.locked && state.gps.nsat < 4)
return;
Point2D.Double ref, lref;
ref = translatePoint(pt, tileCoordOffset(offset));
lref = translatePoint(last_pt, tileCoordOffset(offset));
- tile.show(state, crc_errors, lref, ref);
+ tile.show(state, listener_state, lref, ref);
if (0 <= ref.x && ref.x < px_size)
if (0 <= ref.y && ref.y < px_size)
in_any = true;
lref = translatePoint(last_pt, tileCoordOffset(offset));
AltosSiteMapTile tile = createTile(offset);
- tile.show(state, crc_errors, lref, ref);
- initMap(offset);
- finishTileLater(tile, offset);
+ tile.show(state, listener_state, lref, ref);
+ initAndFinishMapAsync(tile, offset);
}
scrollRocketToVisible(pt);
if (mapTiles.containsKey(offset))
continue;
AltosSiteMapTile tile = createTile(offset);
- initMap(offset);
- finishTileLater(tile, offset);
+ initAndFinishMapAsync(tile, offset);
}
}
}
import javax.swing.*;
import javax.imageio.ImageIO;
+import java.awt.image.*;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
}
try {
- return new ImageIcon(ImageIO.read(pngfile));
+ BufferedImage img;
+
+ img = ImageIO.read(pngfile);
+ if (img == null) {
+ System.out.printf("# Can't read pngfile %s\n", pngfile);
+ return null;
+ }
+ return new ImageIcon(img);
} catch (IOException e) {
System.out.printf("# IO error trying to load %s\n", pngfile);
return null;
private boolean drawn_landed_circle = false;
private boolean drawn_boost_circle = false;
- public synchronized void show(AltosState state, int crc_errors,
+ public synchronized void show(AltosState state, AltosListenerState listener_state,
Point2D.Double last_pt, Point2D.Double pt)
{
if (0 <= state.state && state.state < stateColors.length) {
ALTUSMETRUM_DOC=$(top_srcdir)/doc/altusmetrum.pdf
ALTOS_DOC=$(top_srcdir)/doc/altos.pdf
TELEMETRY_DOC=$(top_srcdir)/doc/telemetry.pdf
-TEMPLATE_DOC=$(top_srcdir)/doc/telemetrum-outline.pdf $(top_srcdir)/doc/megametrum-outline.pdf
+TEMPLATE_DOC=$(top_srcdir)/doc/telemetrum-outline.pdf $(top_srcdir)/doc/telemega-outline.pdf
DOC=$(ALTUSMETRUM_DOC) $(ALTOS_DOC) $(TELEMETRY_DOC) $(TEMPLATE_DOC)
File "../doc/altos.pdf"
File "../doc/telemetry.pdf"
File "../doc/telemetrum-outline.pdf"
- File "../doc/megametrum-outline.pdf"
+ File "../doc/telemega-outline.pdf"
SectionEnd
Section "Uninstaller"
return matchProduct(AltosUILib.product_teledongle) ||
matchProduct(AltosUILib.product_teleterra) ||
matchProduct(AltosUILib.product_telebt) ||
- matchProduct(AltosUILib.product_megadongle);
+ matchProduct(AltosUILib.product_telemega);
if (want_product == AltosUILib.product_altimeter)
return matchProduct(AltosUILib.product_telemetrum) ||
- matchProduct(AltosUILib.product_megametrum);
+ matchProduct(AltosUILib.product_telemega) ||
+ matchProduct(AltosUILib.product_telegps);
int have_product = getProduct();
return device_list;
}
-}
\ No newline at end of file
+}
+++ /dev/null
-# openocd config for MegaMetrum using the Olimex ARM-USB-OCD dongle
-
-source /opt/stm32/share/openocd/scripts/interface/olimex-arm-usb-ocd.cfg
-source /opt/stm32/share/openocd/scripts/target/stm32l.cfg
+++ /dev/null
-target remote localhost:3333
-monitor poll
--- /dev/null
+# openocd config for TeleMega using the Olimex ARM-USB-OCD dongle
+
+source /opt/stm32/share/openocd/scripts/interface/olimex-arm-usb-ocd.cfg
+source /opt/stm32/share/openocd/scripts/target/stm32l.cfg
--- /dev/null
+target remote localhost:3333
+monitor poll
$AOLOAD -D 100 --cal $CAL_VALUE /usr/share/altos/telebt-v0.1*.ihx $SERIAL
echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE
-echo "Unplug and replug USB, cu to the board, confirm freq and record power"
+echo "Unplug debug cable, power cycle, cu to the board, confirm freq and record power"
SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list \
ao-load ao-telem ao-stmload ao-send-telem ao-sky-flash \
- ao-dumpflash ao-edit-telem
+ ao-dumpflash ao-edit-telem ao-dump-up
--- /dev/null
+bin_PROGRAMS=ao-dump-up
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS)
+AO_DUMP_LOG_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_dump_up_DEPENDENCIES = $(AO_DUMP_LOG_LIBS)
+
+ao_dump_up_LDADD=$(AO_DUMP_LOG_LIBS) $(LIBUSB_LIBS) $(GNOME_LIBS)
+
+ao_dump_up_SOURCES = ao-dump-up.c
+
+man_MANS = ao-dump-up.1
--- /dev/null
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-DUMPLOG 1 "ao-dump-up" ""
+.SH NAME
+ao-dump-up \- Dump flight log from MicroPeak flight computer
+.SH SYNOPSIS
+.B "ao-dump-up"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device ao-dump-up uses to communicate with
+the target device.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This forces the program to look
+for a specific USB device name.
+.SH DESCRIPTION
+.I ao-dump-up
+downloads a MicroPeak flight log from a connected MicroPeak USB adapter.
+.SH USAGE
+.I ao-dump-up
+connects to the specified target device and dumps the stored flight
+log.
+.SH AUTHOR
+Keith Packard
--- /dev/null
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include "cc-usb.h"
+#include "cc.h"
+
+#define NUM_BLOCK 512
+
+static const struct option options[] = {
+ { .name = "tty", .has_arg = 1, .val = 'T' },
+ { .name = "device", .has_arg = 1, .val = 'D' },
+ { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+ fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>]\n", program);
+ exit(1);
+}
+
+static uint8_t
+log_checksum(int d[8])
+{
+ uint8_t sum = 0x5a;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ sum += (uint8_t) d[i];
+ return -sum;
+}
+
+static int get_nonwhite(struct cc_usb *cc, int timeout)
+{
+ int c;
+
+ for (;;) {
+ c = cc_usb_getchar_timeout(cc, timeout);
+ putchar(c);
+ if (!isspace(c))
+ return c;
+ }
+}
+
+static uint8_t
+get_hexc(struct cc_usb *cc)
+{
+ int c = get_nonwhite(cc, 1000);
+
+ if ('0' <= c && c <= '9')
+ return c - '0';
+ if ('a' <= c && c <= 'f')
+ return c - 'a' + 10;
+ if ('A' <= c && c <= 'F')
+ return c - 'A' + 10;
+ fprintf(stderr, "Non-hex char '%c'\n", c);
+ exit(1);
+}
+
+static int file_crc;
+
+static const int POLY = 0x8408;
+
+static int
+log_crc(int crc, int b)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (((crc & 0x0001) ^ (b & 0x0001)) != 0)
+ crc = (crc >> 1) ^ POLY;
+ else
+ crc = crc >> 1;
+ b >>= 1;
+ }
+ return crc & 0xffff;
+}
+
+static uint8_t
+get_hex(struct cc_usb *cc)
+{
+ int a = get_hexc(cc);
+ int b = get_hexc(cc);
+ int h = (a << 4) + b;
+
+ file_crc = log_crc(file_crc, h);
+ return h;
+}
+
+static int get_32(struct cc_usb *cc)
+{
+ int v = 0;
+ int i;
+ for (i = 0; i < 4; i++) {
+ v += get_hex(cc) << (i * 8);
+ }
+ return v;
+}
+
+static int get_16(struct cc_usb *cc)
+{
+ int v = 0;
+ int i;
+ for (i = 0; i < 2; i++) {
+ v += get_hex(cc) << (i * 8);
+ }
+ return v;
+}
+
+static int swap16(int i)
+{
+ return ((i << 8) & 0xff00) | ((i >> 8) & 0xff);
+}
+
+static int find_header(struct cc_usb *cc)
+{
+ for (;;) {
+ if (get_nonwhite(cc, 0) == 'M' && get_nonwhite(cc, 1000) == 'P')
+ return 1;
+ }
+}
+
+static const char *state_names[] = {
+ "startup",
+ "idle",
+ "pad",
+ "boost",
+ "fast",
+ "coast",
+ "drogue",
+ "main",
+ "landed",
+ "invalid"
+};
+
+
+int
+main (int argc, char **argv)
+{
+ struct cc_usb *cc;
+ char *tty = NULL;
+ char *device = NULL;
+ int c;
+ char line[8192];
+ int nsamples;
+ int i;
+ int crc;
+ int current_crc;
+
+ while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) {
+ switch (c) {
+ case 'T':
+ tty = optarg;
+ break;
+ case 'D':
+ device = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ if (!tty)
+ tty = cc_usbdevs_find_by_arg(device, "FT230X Basic UART");
+ if (!tty)
+ tty = getenv("ALTOS_TTY");
+ if (!tty)
+ tty="/dev/ttyUSB0";
+ cc = cc_usb_open(tty);
+ if (!cc)
+ exit(1);
+ find_header(cc);
+ file_crc = 0xffff;
+ get_32(cc); /* ground pressure */
+ get_32(cc); /* min pressure */
+ nsamples = get_16(cc); /* nsamples */
+ for (i = 0; i < nsamples; i++)
+ get_16(cc); /* sample i */
+ current_crc = swap16(~file_crc & 0xffff);
+ crc = get_16(cc); /* crc */
+ putchar ('\n');
+ if (crc == current_crc)
+ printf("CRC valid\n");
+ else
+ printf("CRC invalid\n");
+ cc_usb_close(cc);
+ exit (0);
+}
#include "cc.h"
static const struct option options[] = {
+ { .name = "crc", .has_arg = 0, .val = 'c' },
{ 0, 0, 0, 0},
};
char *s;
FILE *file;
int serial;
- while ((c = getopt_long(argc, argv, "", options, NULL)) != -1) {
+ int ignore_crc = 0;
+ while ((c = getopt_long(argc, argv, "c", options, NULL)) != -1) {
switch (c) {
+ case 'c':
+ ignore_crc = 1;
+ break;
default:
usage(argv[0]);
break;
printf ("serial %5d rssi %d status %02x tick %5d type %3d ",
telem.generic.serial, rssi, telem.generic.status,
telem.generic.tick, telem.generic.type);
- if ((telem.generic.status & (1 << 7)) == 0) {
+ if (!ignore_crc && (telem.generic.status & (1 << 7)) == 0) {
printf ("CRC error\n");
continue;
}
}
int
-cc_usb_getchar(struct cc_usb *cc)
+cc_usb_getchar_timeout(struct cc_usb *cc, int timeout)
{
while (cc->in_pos == cc->in_count) {
- if (_cc_usb_sync(cc, 5000) < 0) {
+ if (_cc_usb_sync(cc, timeout) < 0) {
fprintf(stderr, "USB link timeout\n");
exit(1);
}
return cc->in_buf[cc->in_pos++];
}
+int
+cc_usb_getchar(struct cc_usb *cc)
+{
+ return cc_usb_getchar_timeout(cc, 5000);
+}
+
void
cc_usb_getline(struct cc_usb *cc, char *line, int max)
{
tcgetattr(cc->fd, &termios);
save_termios = termios;
cfmakeraw(&termios);
+ cfsetospeed(&termios, B9600);
+ cfsetispeed(&termios, B9600);
tcsetattr(cc->fd, TCSAFLUSH, &termios);
cc_usb_printf(cc, "\nE 0\nm 0\n");
do {
void
cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len);
+int
+cc_usb_getchar_timeout(struct cc_usb *cc, int timeout);
+
int
cc_usb_getchar(struct cc_usb *cc);
/* Check for tty/ttyACMx style names
*/
tty_dir = cc_fullname(endpoint_full, "tty");
- free(endpoint_full);
ntty = scandir(tty_dir, &namelist,
dir_filter_tty,
alphasort);
free (tty_dir);
+ if (ntty > 0) {
+ tty = cc_fullname("/dev", namelist[0]->d_name);
+ free(endpoint_full);
+ free(namelist);
+ return tty;
+ }
+
+ /* Check for ttyACMx style names
+ */
+ ntty = scandir(endpoint_full, &namelist,
+ dir_filter_tty,
+ alphasort);
+ free(endpoint_full);
if (ntty > 0) {
tty = cc_fullname("/dev", namelist[0]->d_name);
free(namelist);
return 1;
}
+static int
+is_am(int idVendor, int idProduct) {
+ if (idVendor == 0xfffe)
+ return 1;
+ if (idVendor == 0x0403 && idProduct == 0x6015)
+ return 1;
+ return 0;
+}
+
struct cc_usbdevs *
cc_usbdevs_scan(void)
{
dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
dev = usb_scan_device(dir);
free(dir);
- if (dev->idVendor == 0xfffe && dev->tty) {
+ if (is_am(dev->idVendor, dev->idProduct) && dev->tty) {
if (devs->dev)
devs->dev = realloc(devs->dev,
(devs->ndev + 1) * sizeof (struct usbdev *));
ao-tools/ao-sky-flash/Makefile
ao-tools/ao-dumpflash/Makefile
ao-tools/ao-edit-telem/Makefile
+ao-tools/ao-dump-up/Makefile
ao-utils/Makefile
src/Version
])
doc/altos.html
doc/altos.pdf
doc/telemetrum-outline.pdf
-doc/megametrum-outline.pdf
+doc/telemega-outline.pdf
STM32L series from ST Microelectronics. This ARM Cortex-M3
based microcontroller offers low power consumption and a
wide variety of built-in peripherals. Altus Metrum uses
- this in the MegaMetrum, MegaDongle and TeleLCO projects.
+ this in the TeleMega, MegaDongle and TeleLCO projects.
</para>
</listitem>
<listitem>
"/usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd">
<book>
<title>The Altus Metrum System</title>
- <subtitle>An Owner's Manual for TeleMetrum, TeleMini and TeleDongle Devices</subtitle>
+ <subtitle>An Owner's Manual for TeleMetrum, TeleMini, TeleDongle and TeleBT Devices</subtitle>
<bookinfo>
<author>
<firstname>Bdale</firstname>
</para>
</legalnotice>
<revhistory>
+ <revision>
+ <revnumber>1.2</revnumber>
+ <date>14 April 2013</date>
+ <revremark>
+ Updated for software version 1.2. Version 1.2 adds support
+ for TeleBT and AltosDroid. It also adds a few minor features
+ and fixes a few minor bugs in AltosUI and the AltOS firmware.
+ </revremark>
+ </revision>
<revision>
<revnumber>1.1.1</revnumber>
<date>16 September 2012</date>
air-frame.
</para>
<para>
- Complementing TeleMetrum and TeleMini is TeleDongle, a USB to RF
- interface for communicating with the altimeters. Combined with your
- choice of antenna and
- notebook computer, TeleDongle and our associated user interface software
- form a complete ground station capable of logging and displaying in-flight
- telemetry, aiding rocket recovery, then processing and archiving flight
+ TeleDongle was our first ground station, providing a USB to RF
+ interfaces for communicating with the altimeters. Combined with
+ your choice of antenna and notebook computer, TeleDongle and our
+ associated user interface software form a complete ground
+ station capable of logging and displaying in-flight telemetry,
+ aiding rocket recovery, then processing and archiving flight
data for analysis and review.
</para>
+ <para>
+ For a slightly more portable ground station experience that also
+ provides direct rocket recovery support, TeleBT offers flight
+ monitoring and data logging using a Bluetooth connection between
+ the receiver and an Android device that has the Altos Droid
+ application installed from the Google Play store.
+ </para>
<para>
More products will be added to the Altus Metrum family over time, and
we currently envision that this will be a single, comprehensive manual
The latest version may always be downloaded from
<ulink url="http://altusmetrum.org/AltOS"/>.
</para>
+ <para>
+ If you're using a TeleBT instead of the TeleDongle, you'll want
+ to go install the Altos Droid application from the Google Play
+ store. You don't need a data plan to use Altos Droid, but
+ without network access, the Map view will be less useful as it
+ won't contain any map data. You can also use TeleBT connected
+ over USB with your laptop computer; it acts exactly like a
+ TeleDongle. Anywhere this manual talks about TeleDongle, you can
+ also read that as 'and TeleBT when connected via USB'.
+ </para>
</chapter>
<chapter>
<title>Handling Precautions</title>
flights, do what makes sense.
</para>
<para>
- If idle mode is entered, you will hear an audible "di-dit" or see
- two short flashes ("I" for idle), and the flight state machine is
- disengaged, thus no ejection charges will fire. The altimeters also
- listen for the radio link when in idle mode for requests sent via
- TeleDongle. Commands can be issued to a TeleMetrum in idle mode
- over either
- USB or the radio link equivalently. TeleMini only has the radio link.
- Idle mode is useful for configuring the altimeter, for extracting data
- from the on-board storage chip after flight, and for ground testing
+ If idle mode is entered, you will hear an audible "di-dit" or
+ see two short flashes ("I" for idle), and the flight state
+ machine is disengaged, thus no ejection charges will fire.
+ The altimeters also listen for the radio link when in idle
+ mode for requests sent via TeleDongle. Commands can be issued
+ to a TeleMetrum in idle mode over either USB or the radio link
+ equivalently. TeleMini only has the radio link. Idle mode is
+ useful for configuring the altimeter, for extracting data from
+ the on-board storage chip after flight, and for ground testing
pyro charges.
</para>
<para>
<section>
<title>Controlling An Altimeter Over The Radio Link</title>
<para>
- One of the unique features of the Altus Metrum system is
- the ability to create a two way command link between TeleDongle
- and an altimeter using the digital radio transceivers built into
- each device. This allows you to interact with the altimeter from
- afar, as if it were directly connected to the computer.
+ One of the unique features of the Altus Metrum system is the
+ ability to create a two way command link between TeleDongle
+ and an altimeter using the digital radio transceivers
+ built into each device. This allows you to interact with the
+ altimeter from afar, as if it were directly connected to the
+ computer.
</para>
<para>
Any operation which can be performed with TeleMetrum can
</para>
</section>
</chapter>
+ <chapter>
+ <title>AltosDroid</title>
+ <para>
+ AltosDroid provides the same flight monitoring capabilities as
+ AltosUI, but runs on Android devices and is designed to connect
+ to a TeleBT receiver over Bluetoothâ„¢. Altos Droid monitors
+ telemetry data, logging it to internal storage in the Android
+ device, and presents that data in a UI the same way the 'Monitor
+ Flight' window does in AltosUI.
+ </para>
+ <para>
+ This manual will explain how to configure AltosDroid, connect
+ to TeleBT, operate the flight monitoring interface and describe
+ what the displayed data means.
+ </para>
+ <section>
+ <title>Installing AltosDroid</title>
+ <para>
+ AltosDroid is included in the Google Play store. To install
+ it on your Android device, open open the Google Play Store
+ application and search for "altosdroid". Make sure you don't
+ have a space between "altos" and "droid" or you probably won't
+ find what you want. That should bring you to the right page
+ from which you can download and install the application.
+ </para>
+ </section>
+ <section>
+ <title>Connecting to TeleBT</title>
+ <para>
+ Press the Android 'Menu' button or soft-key to see the
+ configuration options available. Select the 'Connect a device'
+ option and then the 'Scan for devices' entry at the bottom to
+ look for your TeleBT device. Select your device, and when it
+ asks for the code, enter '1234'.
+ </para>
+ <para>
+ Subsequent connections will not require you to enter that
+ code, and your 'paired' device will appear in the list without
+ scanning.
+ </para>
+ </section>
+ <section>
+ <title>Configuring AltosDroid</title>
+ <para>
+ The only configuration option available for AltosDroid is
+ which frequency to listen on. Press the Android 'Menu' button
+ or soft-key and pick the 'Select radio frequency' entry. That
+ brings up a menu of pre-set radio frequencies; pick the one
+ which matches your altimeter.
+ </para>
+ </section>
+ <section>
+ <title>Altos Droid Flight Monitoring</title>
+ <para>
+ Altos Droid is designed to mimic the AltosUI flight monitoring
+ display, providing separate tabs for each stage of your rocket
+ flight along with a tab containing a map of the local area
+ with icons marking the current location of the altimeter and
+ the Android device.
+ </para>
+ <section>
+ <title>Pad</title>
+ <para>
+ The 'Launch Pad' tab shows information used to decide when the
+ rocket is ready for flight. The first elements include red/green
+ indicators, if any of these is red, you'll want to evaluate
+ whether the rocket is ready to launch:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Battery Voltage. This indicates whether the Li-Po battery
+ powering the TeleMetrum has sufficient charge to last for
+ the duration of the flight. A value of more than
+ 3.7V is required for a 'GO' status.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Apogee Igniter Voltage. This indicates whether the apogee
+ igniter has continuity. If the igniter has a low
+ resistance, then the voltage measured here will be close
+ to the Li-Po battery voltage. A value greater than 3.2V is
+ required for a 'GO' status.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Main Igniter Voltage. This indicates whether the main
+ igniter has continuity. If the igniter has a low
+ resistance, then the voltage measured here will be close
+ to the Li-Po battery voltage. A value greater than 3.2V is
+ required for a 'GO' status.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ On-board Data Logging. This indicates whether there is
+ space remaining on-board to store flight data for the
+ upcoming flight. If you've downloaded data, but failed
+ to erase flights, there may not be any space
+ left. TeleMetrum can store multiple flights, depending
+ on the configured maximum flight log size. TeleMini
+ stores only a single flight, so it will need to be
+ downloaded and erased after each flight to capture
+ data. This only affects on-board flight logging; the
+ altimeter will still transmit telemetry and fire
+ ejection charges at the proper times.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ GPS Locked. For a TeleMetrum device, this indicates whether the GPS receiver is
+ currently able to compute position information. GPS requires
+ at least 4 satellites to compute an accurate position.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ GPS Ready. For a TeleMetrum device, this indicates whether GPS has reported at least
+ 10 consecutive positions without losing lock. This ensures
+ that the GPS receiver has reliable reception from the
+ satellites.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ The Launchpad tab also shows the computed launch pad position
+ and altitude, averaging many reported positions to improve the
+ accuracy of the fix.
+ </para>
+ </para>
+ </section>
+ </section>
+ <section>
+ <title>Downloading Flight Logs</title>
+ <para>
+ Altos Droid always saves every bit of telemetry data it
+ receives. To download that to a computer for use with AltosUI,
+ simply remove the SD card from your Android device, or connect
+ your device to your computer's USB port and browse the files
+ on that device. You will find '.telem' files in the TeleMetrum
+ directory that will work with AltosUI directly.
+ </para>
+ </section>
+ </chapter>
<chapter>
<title>Using Altus Metrum Products</title>
<section>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="427.5"
- height="270"
- id="svg2"
- version="1.1"
- inkscape:version="0.48.3.1 r9886"
- sodipodi:docname="megametrum-outline.svg">
- <defs
- id="defs4">
- <marker
- inkscape:stockid="Arrow2Lend"
- orient="auto"
- refY="0.0"
- refX="0.0"
- id="Arrow2Lend"
- style="overflow:visible;">
- <path
- id="path3866"
- style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
- d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
- transform="scale(1.1) rotate(180) translate(1,0)" />
- </marker>
- <inkscape:perspective
- sodipodi:type="inkscape:persp3d"
- inkscape:vp_x="0 : 526.18109 : 1"
- inkscape:vp_y="0 : 1000 : 0"
- inkscape:vp_z="744.09448 : 526.18109 : 1"
- inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
- id="perspective10" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="2.1783851"
- inkscape:cx="315.40175"
- inkscape:cy="122.33575"
- inkscape:document-units="in"
- inkscape:current-layer="layer1"
- showgrid="true"
- inkscape:window-width="1527"
- inkscape:window-height="1313"
- inkscape:window-x="813"
- inkscape:window-y="166"
- inkscape:window-maximized="0"
- units="in"
- showguides="true"
- inkscape:guide-bbox="true">
- <sodipodi:guide
- position="0,0"
- orientation="0,427.5"
- id="guide3005" />
- <sodipodi:guide
- position="427.5,0"
- orientation="-270,0"
- id="guide3007" />
- <sodipodi:guide
- position="427.5,270"
- orientation="0,-427.5"
- id="guide3009" />
- <sodipodi:guide
- position="0,270"
- orientation="270,0"
- id="guide3011" />
- <inkscape:grid
- type="xygrid"
- id="grid3013"
- empspacing="4"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true"
- units="in"
- spacingx="0.025in"
- spacingy="0.025in" />
- </sodipodi:namedview>
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(0,-782.35975)">
- <rect
- style="fill:none;stroke:#000000;stroke-width:0.54555845;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- id="rect2816"
- width="291.95444"
- height="111.95444"
- x="90.272781"
- y="850.13251" />
- <g
- inkscape:tile-y0="681.11218"
- inkscape:tile-x0="90"
- id="use3601"
- transform="matrix(0.97131843,0,0,0.97528987,8.397686,191.32255)">
- <path
- sodipodi:type="arc"
- style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- id="path3611"
- sodipodi:cx="116"
- sodipodi:cy="739.36218"
- sodipodi:rx="17"
- sodipodi:ry="18"
- d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
- transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
- <path
- style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 95.625,692.36218 0,-11.25"
- id="path3613"
- sodipodi:nodetypes="cc"
- inkscape:connector-curvature="0" />
- <path
- sodipodi:nodetypes="cc"
- id="path3615"
- d="m 90,686.73718 11.25,0"
- style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- inkscape:connector-curvature="0" />
- </g>
- <g
- inkscape:tile-y0="681.11218"
- inkscape:tile-x0="90"
- id="use3603"
- transform="matrix(0.97186116,0,0,0.97241431,278.34851,193.3134)">
- <path
- sodipodi:type="arc"
- style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- id="path3619"
- sodipodi:cx="116"
- sodipodi:cy="739.36218"
- sodipodi:rx="17"
- sodipodi:ry="18"
- d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
- transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
- <path
- style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 95.625,692.36218 0,-11.25"
- id="path3621"
- sodipodi:nodetypes="cc"
- inkscape:connector-curvature="0" />
- <path
- sodipodi:nodetypes="cc"
- id="path3623"
- d="m 90,686.73718 11.25,0"
- style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- inkscape:connector-curvature="0" />
- </g>
- <g
- inkscape:tile-y0="681.11218"
- inkscape:tile-x0="90"
- id="use3605"
- transform="matrix(0.97475506,0,0,0.97241431,278.08835,283.31323)">
- <path
- sodipodi:type="arc"
- style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- id="path3627"
- sodipodi:cx="116"
- sodipodi:cy="739.36218"
- sodipodi:rx="17"
- sodipodi:ry="18"
- d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
- transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
- <path
- style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 95.625,692.36218 0,-11.25"
- id="path3629"
- sodipodi:nodetypes="cc"
- inkscape:connector-curvature="0" />
- <path
- sodipodi:nodetypes="cc"
- id="path3631"
- d="m 90,686.73718 11.25,0"
- style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- inkscape:connector-curvature="0" />
- </g>
- <g
- inkscape:tile-y0="681.11218"
- inkscape:tile-x0="90"
- id="use3607"
- transform="matrix(0.97186116,0,0,0.97241431,8.3485628,283.31356)">
- <path
- sodipodi:type="arc"
- style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- id="path3635"
- sodipodi:cx="116"
- sodipodi:cy="739.36218"
- sodipodi:rx="17"
- sodipodi:ry="18"
- d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
- transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
- <path
- style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 95.625,692.36218 0,-11.25"
- id="path3637"
- sodipodi:nodetypes="cc"
- inkscape:connector-curvature="0" />
- <path
- sodipodi:nodetypes="cc"
- id="path3639"
- d="m 90,686.73718 11.25,0"
- style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- inkscape:connector-curvature="0" />
- </g>
- <path
- style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- d="m 135,903.85975 157.5,0"
- id="path2829"
- sodipodi:nodetypes="cc"
- inkscape:connector-curvature="0" />
- <text
- xml:space="preserve"
- style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Minion Pro;-inkscape-font-specification:Minion Pro"
- x="888.10974"
- y="-303.75"
- id="text4236"
- sodipodi:linespacing="125%"
- transform="matrix(0,1,-1,0,0,0)"><tspan
- sodipodi:role="line"
- x="888.10974"
- y="-303.75"
- id="tspan4242">UP</tspan></text>
- </g>
-</svg>
</listitem>
<listitem>
<para>
- The MicroPeak USB adapter has a small phototransistor on the
- end of the board furthest from the USB connector. Locate
- this and place the LED on the MicroPeak right over
- it. Turn on the MicroPeak board and adjust the position
- until the blue LED on the MicroPeak USB adapter blinks in
- time with the orange LED on the MicroPeak board.
+ The MicroPeak USB adapter has a small phototransistor on
+ the end of the board furthest from the USB
+ connector. Locate this and place the LED on the MicroPeak
+ directly in contact with it. The MicroPeak LED and the
+ MicroPeak USB adapter photo need to be touching—even a
+ millimeters of space between them will reduce the light
+ intensity from the LED enough that the phototransistor
+ will not sense it. Turn on the MicroPeak board and adjust
+ the position until the blue LED on the MicroPeak USB
+ adapter blinks in time with the orange LED on the
+ MicroPeak board.
</para>
</listitem>
<listitem>
raised, breaking the Descent tab contents.
</listitem>
<listitem>
- Add preliminary MegaMetrum support, including configuration,
+ Add preliminary TeleMega support, including configuration,
data download and analysis.
</listitem>
<listitem>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="427.5"
+ height="270"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="megametrum-outline.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Lend"
+ style="overflow:visible;">
+ <path
+ id="path3866"
+ style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(1.1) rotate(180) translate(1,0)" />
+ </marker>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.1783851"
+ inkscape:cx="315.40175"
+ inkscape:cy="122.33575"
+ inkscape:document-units="in"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1527"
+ inkscape:window-height="1313"
+ inkscape:window-x="813"
+ inkscape:window-y="166"
+ inkscape:window-maximized="0"
+ units="in"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <sodipodi:guide
+ position="0,0"
+ orientation="0,427.5"
+ id="guide3005" />
+ <sodipodi:guide
+ position="427.5,0"
+ orientation="-270,0"
+ id="guide3007" />
+ <sodipodi:guide
+ position="427.5,270"
+ orientation="0,-427.5"
+ id="guide3009" />
+ <sodipodi:guide
+ position="0,270"
+ orientation="270,0"
+ id="guide3011" />
+ <inkscape:grid
+ type="xygrid"
+ id="grid3013"
+ empspacing="4"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true"
+ units="in"
+ spacingx="0.025in"
+ spacingy="0.025in" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-782.35975)">
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:0.54555845;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect2816"
+ width="291.95444"
+ height="111.95444"
+ x="90.272781"
+ y="850.13251" />
+ <g
+ inkscape:tile-y0="681.11218"
+ inkscape:tile-x0="90"
+ id="use3601"
+ transform="matrix(0.97131843,0,0,0.97528987,8.397686,191.32255)">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3611"
+ sodipodi:cx="116"
+ sodipodi:cy="739.36218"
+ sodipodi:rx="17"
+ sodipodi:ry="18"
+ d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+ transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+ <path
+ style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 95.625,692.36218 0,-11.25"
+ id="path3613"
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path3615"
+ d="m 90,686.73718 11.25,0"
+ style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ inkscape:tile-y0="681.11218"
+ inkscape:tile-x0="90"
+ id="use3603"
+ transform="matrix(0.97186116,0,0,0.97241431,278.34851,193.3134)">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3619"
+ sodipodi:cx="116"
+ sodipodi:cy="739.36218"
+ sodipodi:rx="17"
+ sodipodi:ry="18"
+ d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+ transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+ <path
+ style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 95.625,692.36218 0,-11.25"
+ id="path3621"
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path3623"
+ d="m 90,686.73718 11.25,0"
+ style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ inkscape:tile-y0="681.11218"
+ inkscape:tile-x0="90"
+ id="use3605"
+ transform="matrix(0.97475506,0,0,0.97241431,278.08835,283.31323)">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3627"
+ sodipodi:cx="116"
+ sodipodi:cy="739.36218"
+ sodipodi:rx="17"
+ sodipodi:ry="18"
+ d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+ transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+ <path
+ style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 95.625,692.36218 0,-11.25"
+ id="path3629"
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path3631"
+ d="m 90,686.73718 11.25,0"
+ style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ inkscape:tile-y0="681.11218"
+ inkscape:tile-x0="90"
+ id="use3607"
+ transform="matrix(0.97186116,0,0,0.97241431,8.3485628,283.31356)">
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3635"
+ sodipodi:cx="116"
+ sodipodi:cy="739.36218"
+ sodipodi:rx="17"
+ sodipodi:ry="18"
+ d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+ transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+ <path
+ style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 95.625,692.36218 0,-11.25"
+ id="path3637"
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path3639"
+ d="m 90,686.73718 11.25,0"
+ style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 135,903.85975 157.5,0"
+ id="path2829"
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Minion Pro;-inkscape-font-specification:Minion Pro"
+ x="888.10974"
+ y="-303.75"
+ id="text4236"
+ sodipodi:linespacing="125%"
+ transform="matrix(0,1,-1,0,0,0)"><tspan
+ sodipodi:role="line"
+ x="888.10974"
+ y="-303.75"
+ id="tspan4242">UP</tspan></text>
+ </g>
+</svg>
altos_bt_open(struct altos_bt_device *device)
{
struct sockaddr_rc addr = { 0 };
- int s, status;
+ int status, i;
struct altos_file *file;
file = calloc(1, sizeof (struct altos_file));
if (!file)
goto no_file;
- file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
- if (file->fd < 0) {
- altos_set_last_posix_error();
- goto no_sock;
- }
-
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = 1;
str2ba(device->addr, &addr.rc_bdaddr);
- status = connect(file->fd,
- (struct sockaddr *)&addr,
- sizeof(addr));
+ for (i = 0; i < 5; i++) {
+ file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (file->fd < 0) {
+ altos_set_last_posix_error();
+ goto no_sock;
+ }
+
+ status = connect(file->fd,
+ (struct sockaddr *)&addr,
+ sizeof(addr));
+ if (status >= 0 || errno != EBUSY)
+ break;
+ close(file->fd);
+ usleep(100 * 1000);
+ }
if (status < 0) {
altos_set_last_posix_error();
goto no_link;
}
- sleep(1);
+ usleep(100 * 1000);
#ifdef USE_POLL
pipe(file->pipe);
#endif
return file;
no_link:
- close(s);
+ close(file->fd);
no_sock:
free(file);
no_file:
struct altos_file *file = calloc (1, sizeof (struct altos_file));
char full_name[64];
COMMTIMEOUTS timeouts;
+ int i;
if (!file)
return NULL;
strcpy(full_name, "\\\\.\\");
strcat(full_name, device->path);
- file->handle = open_serial(full_name);
+ file->handle = INVALID_HANDLE_VALUE;
+
+ for (i = 0; i < 5; i++) {
+ file->handle = open_serial(full_name);
+ if (file->handle != INVALID_HANDLE_VALUE)
+ break;
+ Sleep(100);
+ }
+
if (file->handle == INVALID_HANDLE_VALUE) {
free(file);
return NULL;
serial.close();
serial_thread.interrupt();
}
+ setVisible(false);
}
public MicroDownload(MicroPeak owner, AltosDevice device) {
super (owner, "Download MicroPeak Data", false);
+ int y = 0;
+
GridBagConstraints c;
Insets il = new Insets(4,4,4,4);
Insets ir = new Insets(4,4,4,4);
pane.setLayout(new GridBagLayout());
c = new GridBagConstraints();
- c.gridx = 0; c.gridy = 0;
+ c.gridx = 0; c.gridy = y;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
pane.add(device_label, c);
c = new GridBagConstraints();
- c.gridx = 1; c.gridy = 0;
+ c.gridx = 1; c.gridy = y;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = ir;
JLabel device_value = new JLabel(device.toString());
pane.add(device_value, c);
+ y++;
+
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = y;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ JTextArea help_text = new JTextArea(
+
+ "Locate the photo transistor on the MicroPeak USB adapter\n" +
+ "and place the LED on the MicroPeak directly in contact\n" +
+ "with it.\n" +
+ "\n" +
+ "The MicroPeak LED and the MicroPeak USB adapter\n" +
+ "photo need to be touching—even a millimeters of space\n" +
+ "between them will reduce the light intensity from the LED\n" +
+ "enough that the phototransistor will not sense it.\n" +
+ "\n" +
+ "Turn on the MicroPeak board and adjust the position until\n" +
+ "the blue LED on the MicroPeak USB adapter blinks in time\n" +
+ "with the orange LED on the MicroPeak board.");
+
+ pane.add(help_text, c);
+ y++;
+
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = y;
+ c.gridwidth = 1;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ JLabel waiting_value = new JLabel("Waiting for MicroPeak data...");
+ pane.add(waiting_value, c);
cancel = new JButton("Cancel");
c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
- c.gridx = 0; c.gridy = 1;
+ c.gridx = 1; c.gridy = y;
c.gridwidth = GridBagConstraints.REMAINDER;
Insets ic = new Insets(4,4,4,4);
c.insets = ic;
pane.add(cancel, c);
+ y++;
cancel.addActionListener(this);
pack();
setLocationRelativeTo(owner);
setVisible(true);
- this.start();
+ start();
}
}
telemetrum-v1.2 telemetrum-v1.1 telemetrum-v1.0 \
teledongle-v0.2 teledongle-v0.1 \
telemini-v1.0 telenano-v0.1 \
- telebt-v0.0 telebt-v0.1 \
+ telebt-v1.0 \
telemetrum-v0.1-sky telemetrum-v0.1-sirf \
telelaunch-v0.1 tidongle test \
teleterra-v0.2 teleshield-v0.1 \
telescience-v0.1 telescience-pwm telepyro-v0.1 micropeak
ARMDIRS=\
- megametrum-v0.1 megadongle-v0.1 stm-bringup stm-demo telelco-v0.1 \
+ telemega-v0.1 megadongle-v0.1 stm-bringup stm-demo telelco-v0.1 \
telescience-v0.2
ifneq ($(shell which sdcc),)
#define GOT_ADC
#endif /* TELEFIRE_V_0_1 */
+#ifdef TELEBT_V_1_0
+ a = (uint8_t __xdata *) (&ao_data_ring[ao_data_head].adc.batt);
+ a[0] = ADCL;
+ a[1] = ADCH;
+ if (0)
+ ;
+#define GOT_ADC
+#endif
+
#ifndef GOT_ADC
#error No known ADC configuration set
#endif
#define AO_PANIC_STACK 12 /* Stack overflow */
#define AO_PANIC_SPI 13 /* SPI communication failure */
#define AO_PANIC_CRASH 14 /* Processor crashed */
+#define AO_PANIC_BUFIO 15 /* Mis-using bufio API */
#define AO_PANIC_SELF_TEST_CC1120 0x40 | 1 /* Self test failure */
#define AO_PANIC_SELF_TEST_HMC5883 0x40 | 2 /* Self test failure */
#define AO_PANIC_SELF_TEST_MPU6000 0x40 | 3 /* Self test failure */
#define AO_SEC_TO_TICKS(s) ((s) * AO_HERTZ)
/* Returns the current time in ticks */
-uint16_t
+AO_TICK_TYPE
ao_time(void);
/* Suspend the current task until ticks time has passed */
#define AO_RADIO_STATUS_CRC_OK AO_FEC_DECODE_CRC_OK
#endif
+#ifndef HAS_RADIO_RECV
+#define HAS_RADIO_RECV HAS_RADIO
+#endif
+#ifndef HAS_RADIO_XMIT
+#define HAS_RADIO_XMIT HAS_RADIO
+#endif
+
void
ao_radio_general_isr(void) ao_arch_interrupt(16);
+#if HAS_RADIO_XMIT
void
ao_radio_send(const __xdata void *d, uint8_t size) __reentrant;
+#endif
+#if HAS_RADIO_RECV
uint8_t
ao_radio_recv(__xdata void *d, uint8_t size) __reentrant;
void
ao_radio_recv_abort(void);
+#endif
void
ao_radio_test(uint8_t on);
typedef int16_t (*ao_radio_fill_func)(uint8_t *buffer, int16_t len);
void
-ao_radio_send_lots(ao_radio_fill_func fill);
+ao_radio_send_aprs(ao_radio_fill_func fill);
+
+/*
+ * ao_radio_pa
+ */
+
+#if HAS_RADIO_AMP
+void
+ao_radio_pa_on(void);
+
+void
+ao_radio_pa_off(void);
+
+void
+ao_radio_pa_init(void);
+#else
+#define ao_radio_pa_on()
+#define ao_radio_pa_off()
+#define ao_radio_pa_init()
+#endif
/*
* Compute the packet length as follows:
#endif
#define AO_CONFIG_MAJOR 1
-#define AO_CONFIG_MINOR 13
+#define AO_CONFIG_MINOR 14
#define AO_AES_LEN 16
struct ao_pyro pyro[AO_PYRO_NUM]; /* minor version 12 */
#endif
uint16_t aprs_interval; /* minor version 13 */
+#if HAS_RADIO_POWER
+ uint8_t radio_power; /* minor version 14 */
+#endif
+#if HAS_RADIO_AMP
+ uint8_t radio_amp; /* minor version 14 */
+#endif
};
#define AO_IGNITE_MODE_DUAL 0
#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX ((uint32_t) 192 * (uint32_t) 1024)
#endif
#endif
+#define AO_CONFIG_DEFAULT_RADIO_POWER 0x60
+#define AO_CONFIG_DEFAULT_RADIO_AMP 0
#if HAS_EEPROM
static void
#endif
if (minor < 13)
ao_config.aprs_interval = 0;
+#if HAS_RADIO_POWER
+ if (minor < 14)
+ ao_config.radio_power = AO_CONFIG_DEFAULT_RADIO_POWER;
+ #endif
+#if HAS_RADIO_AMP
+ if (minor < 14)
+ ao_config.radio_amp = AO_CONFIG_DEFAULT_RADIO_AMP;
+#endif
ao_config.minor = AO_CONFIG_MINOR;
ao_config_dirty = 1;
}
}
#if HAS_RADIO
+
void
ao_config_frequency_show(void) __reentrant
{
ao_config.frequency = ao_cmd_lex_u32;
ao_config_set_radio();
_ao_config_edit_finish();
+#if HAS_RADIO_RECV
ao_radio_recv_abort();
+#endif
}
#endif
#endif /* HAS_APRS */
+#if HAS_RADIO_AMP
+
+void
+ao_config_radio_amp_show(void)
+{
+ printf ("Radio amp setting: %d\n", ao_config.radio_amp);
+}
+
+void
+ao_config_radio_amp_set(void)
+{
+ ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ _ao_config_edit_start();
+ ao_config.radio_amp = ao_cmd_lex_i;
+ _ao_config_edit_finish();
+}
+
+#endif
+
+#if HAS_RADIO_POWER
+
+void
+ao_config_radio_power_show(void)
+{
+ printf ("Radio power setting: %d\n", ao_config.radio_power);
+}
+
+void
+ao_config_radio_power_set(void)
+{
+ ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ _ao_config_edit_start();
+ ao_config.radio_power = ao_cmd_lex_i;
+ _ao_config_edit_finish();
+}
+
+#endif
+
struct ao_config_var {
__code char *str;
void (*set)(void) __reentrant;
ao_config_radio_enable_set, ao_config_radio_enable_show },
{ "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
ao_config_radio_cal_set, ao_config_radio_cal_show },
+#if HAS_RADIO_POWER
+ { "p <setting>\0Radio power setting (0-255)",
+ ao_config_radio_power_set, ao_config_radio_power_show },
+#endif
+#if HAS_RADIO_AMP
+ { "d <setting>\0Radio amplifier setting (0-3)",
+ ao_config_radio_amp_set, ao_config_radio_amp_show },
+#endif
#endif /* HAS_RADIO */
#if HAS_ACCEL
{ "a <+g> <-g>\0Accel calib (0 for auto)",
#include <ao_fec.h>
#include <stdio.h>
-#ifdef MEGAMETRUM
+#ifdef TELEMEGA
#include <ao.h>
#endif
*/
#include "ao.h"
+#include "ao_log.h"
void
ao_gps_report_mega(void)
#define AO_LOG_FORMAT_TINY 2 /* two byte state/baro records */
#define AO_LOG_FORMAT_TELEMETRY 3 /* 32 byte ao_telemetry records */
#define AO_LOG_FORMAT_TELESCIENCE 4 /* 32 byte typed telescience records */
-#define AO_LOG_FORMAT_MEGAMETRUM 5 /* 32 byte typed megametrum records */
+#define AO_LOG_FORMAT_TELEMEGA 5 /* 32 byte typed telemega records */
#define AO_LOG_FORMAT_NONE 127 /* No log at all */
extern __code uint8_t ao_log_format;
uint8_t
ao_log_mega(__xdata struct ao_log_mega *log) __reentrant;
+void
+ao_log_flush(void);
+
#endif /* _AO_LOG_H_ */
static __xdata uint8_t ao_log_mutex;
static __xdata struct ao_log_mega log;
-__code uint8_t ao_log_format = AO_LOG_FORMAT_MEGAMETRUM;
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TELEMEGA;
static uint8_t
ao_log_csum(__xdata uint8_t *b) __reentrant
}
#endif
+ ao_log_flush();
+
/* Wait for a while */
ao_delay(AO_MS_TO_TICKS(100));
*/
#include "ao.h"
+#include "ao_log.h"
#include "ao_product.h"
static __pdata uint16_t ao_telemetry_interval;
#include <ao_aprs.h>
#endif
-#if defined(MEGAMETRUM)
+#if defined(TELEMEGA)
#define AO_SEND_MEGA 1
#endif
#ifdef AO_SEND_ALL_BARO
ao_send_baro();
#endif
+#if HAS_FLIGHT
#ifdef AO_SEND_MEGA
ao_send_mega_sensor();
ao_send_mega_data();
#else
ao_send_sensor();
#endif
+#endif
#if HAS_COMPANION
if (ao_companion_running)
tncIndex = 0;
tncMode = TNC_TX_SYNC;
- ao_radio_send_lots(tncFill);
+ ao_radio_send_aprs(tncFill);
}
/** @} */
while (!ao_btm_connected)
ao_sleep(&ao_btm_connected);
while (ao_btm_connected) {
- ao_led_for(AO_LED_GREEN, AO_MS_TO_TICKS(20));
+ ao_led_for(AO_BT_LED, AO_MS_TO_TICKS(20));
ao_delay(AO_SEC_TO_TICKS(3));
}
}
--- /dev/null
+/*
+ * Copyright © 2013 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.
+ */
+
+#ifndef AO_FAT_TEST
+#include "ao.h"
+#endif
+
+/* Include bufio commands */
+#ifndef AO_FAT_TEST
+#define BUFIO_COMMANDS 0
+#endif
+
+#include "ao_sdcard.h"
+#include "ao_bufio.h"
+
+#define AO_NUM_BUF 16
+#define AO_BUFSIZ 512
+
+struct ao_bufio {
+ uint32_t block;
+ int16_t seqno;
+ uint8_t busy;
+ uint8_t dirty;
+};
+
+static struct ao_bufio ao_bufio[AO_NUM_BUF];
+static uint8_t ao_buffer[AO_NUM_BUF][AO_BUFSIZ];
+static int16_t ao_seqno;
+static uint8_t ao_bufio_mutex;
+
+#if 0
+#define DBG(...) printf(__VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+static inline void
+ao_bufio_lock(void)
+{
+ ao_mutex_get(&ao_bufio_mutex);
+}
+
+static inline void
+ao_bufio_unlock(void)
+{
+ ao_mutex_put(&ao_bufio_mutex);
+}
+
+static inline int16_t
+ao_seqno_age(int16_t s)
+{
+ return ao_seqno - s;
+}
+
+static inline int16_t
+ao_seqno_next(void)
+{
+ return ++ao_seqno;
+}
+
+static inline int
+ao_seqno_older(int16_t a, int16_t b)
+{
+ return ao_seqno_age(a) > ao_seqno_age(b);
+}
+
+static inline void
+ao_validate_bufno(int b)
+{
+ if (b < 0 || AO_NUM_BUF <= b)
+ ao_panic(AO_PANIC_BUFIO);
+}
+
+static inline int
+ao_buf_to_num(uint8_t *buf)
+{
+ int b = (buf - &ao_buffer[0][0]) / AO_BUFSIZ;
+
+ ao_validate_bufno(b);
+ return b;
+}
+
+static inline int
+ao_bufio_to_num(struct ao_bufio *bufio)
+{
+ int b = (bufio - ao_bufio);
+
+ ao_validate_bufno(b);
+ return b;
+}
+
+static inline struct ao_bufio *
+ao_buf_to_bufio(uint8_t *buf)
+{
+ int b = ao_buf_to_num(buf);
+ struct ao_bufio *bufio;
+
+ bufio = &ao_bufio[b];
+ DBG ("buf %08x is %d bufio %08x\n", buf, b, bufio);
+ return bufio;
+}
+
+static inline uint8_t *
+ao_bufio_to_buf(struct ao_bufio *bufio)
+{
+ int b = ao_bufio_to_num(bufio);
+ uint8_t *buf;
+
+ buf = &ao_buffer[b][0];
+ DBG ("bufio %08x is %d buf %08x\n", bufio, b, buf);
+ return buf;
+}
+
+/*
+ * Write a buffer to storage if it is dirty
+ */
+static void
+ao_bufio_write(struct ao_bufio *bufio)
+{
+ if (bufio->dirty) {
+ ao_sdcard_write_block(bufio->block, ao_bufio_to_buf(bufio));
+ bufio->dirty = 0;
+ }
+}
+
+/*
+ * Read a buffer from storage
+ */
+static uint8_t
+ao_bufio_read(struct ao_bufio *bufio)
+{
+ uint8_t *buf = ao_bufio_to_buf(bufio);
+
+ return ao_sdcard_read_block(bufio->block, buf);
+}
+
+/*
+ * Find a buffer containing the specified block
+ */
+static struct ao_bufio *
+ao_bufio_find_block(uint32_t block)
+{
+ int b;
+
+ for (b = 0; b < AO_NUM_BUF; b++) {
+ struct ao_bufio *bufio = &ao_bufio[b];
+ if (bufio->block == block) {
+ DBG ("Found existing buffer %d (seqno %d)\n",
+ ao_bufio_to_num(bufio), bufio->seqno);
+ return bufio;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Find the least recently used idle buffer
+ */
+static struct ao_bufio *
+ao_bufio_find_idle(void)
+{
+ int b;
+ struct ao_bufio *oldest = NULL;
+
+ for (b = 0; b < AO_NUM_BUF; b++) {
+ struct ao_bufio *bufio = &ao_bufio[b];
+ if (!bufio->busy)
+ if (!oldest || ao_seqno_older(bufio->seqno, oldest->seqno))
+ oldest = bufio;
+ }
+ if (oldest)
+ DBG ("Using idle buffer %d (seqno %d)\n",
+ ao_bufio_to_num(oldest), oldest->seqno);
+ return oldest;
+}
+
+/*
+ * Return a pointer to a buffer containing
+ * the contents of the specified block
+ */
+uint8_t *
+ao_bufio_get(uint32_t block)
+{
+ struct ao_bufio *bufio;
+ uint8_t *buf = NULL;
+
+ ao_bufio_lock();
+ bufio = ao_bufio_find_block(block);
+ if (!bufio) {
+ bufio = ao_bufio_find_idle();
+ if (bufio) {
+ ao_bufio_write(bufio);
+ bufio->block = block;
+ DBG ("read buffer\n");
+ if (!ao_bufio_read(bufio)) {
+ bufio->block = 0xffffffff;
+ bufio = NULL;
+ }
+ } else
+ ao_panic(AO_PANIC_BUFIO);
+ }
+ if (bufio) {
+ bufio->busy++;
+ if (!bufio->busy)
+ ao_panic(AO_PANIC_BUFIO);
+ buf = ao_bufio_to_buf(bufio);
+ }
+ ao_bufio_unlock();
+ return buf;
+}
+
+/*
+ * Release a buffer, marking it dirty
+ * if it has been written to
+ */
+void
+ao_bufio_put(uint8_t *buf, uint8_t write)
+{
+ struct ao_bufio *bufio;
+
+ ao_bufio_lock();
+ bufio = ao_buf_to_bufio(buf);
+
+ if (!bufio->busy)
+ ao_panic(AO_PANIC_BUFIO);
+
+ DBG ("idle buffer %d write %d\n", ao_bufio_to_num(bufio), write);
+ bufio->dirty |= write;
+ if (!--bufio->busy) {
+ bufio->seqno = ao_seqno_next();
+ DBG ("not busy, seqno %d\n", bufio->seqno);
+ }
+ ao_bufio_unlock();
+}
+
+/*
+ * Flush a single buffer immediately. Useful
+ * if write order is important
+ */
+void
+ao_bufio_flush_one(uint8_t *buf)
+{
+ ao_bufio_lock();
+ ao_bufio_write(ao_buf_to_bufio(buf));
+ ao_bufio_unlock();
+}
+
+/*
+ * Flush all buffers to storage
+ */
+void
+ao_bufio_flush(void)
+{
+ int b;
+
+ ao_bufio_lock();
+ for (b = 0; b < AO_NUM_BUF; b++)
+ ao_bufio_write(&ao_bufio[b]);
+ ao_bufio_unlock();
+}
+
+#if BUFIO_COMMANDS
+static void
+ao_bufio_test_read(void)
+{
+ uint8_t *buf;
+ ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+ if ((buf = ao_bufio_get(ao_cmd_lex_u32))) {
+ int i;
+ for (i = 0; i < 512; i++) {
+ printf (" %02x", buf[i]);
+ if ((i & 0xf) == 0xf)
+ printf("\n");
+ }
+ ao_bufio_put(buf, 0);
+ }
+}
+
+static const struct ao_cmds ao_bufio_cmds[] = {
+ { ao_bufio_test_read, "q\0Test bufio read" },
+ { 0, NULL },
+};
+#endif
+
+void
+ao_bufio_setup(void)
+{
+ int b;
+
+ for (b = 0; b < AO_NUM_BUF; b++) {
+ ao_bufio[b].dirty = 0;
+ ao_bufio[b].busy = 0;
+ ao_bufio[b].block = 0xffffffff;
+ }
+}
+
+void
+ao_bufio_init(void)
+{
+ ao_bufio_setup();
+ ao_sdcard_init();
+#if BUFIO_COMMANDS
+ ao_cmd_register(&ao_bufio_cmds[0]);
+#endif
+}
--- /dev/null
+/*
+ * Copyright © 2013 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.
+ */
+
+#ifndef _AO_BUFIO_H_
+#define _AO_BUFIO_H_
+
+uint8_t *
+ao_bufio_get(uint32_t block);
+
+void
+ao_bufio_put(uint8_t *buf, uint8_t write);
+
+void
+ao_bufio_flush_one(uint8_t *buf);
+
+void
+ao_bufio_flush(void);
+
+void
+ao_bufio_setup(void);
+
+void
+ao_bufio_init(void);
+
+#endif /* _AO_BUFIO_H_ */
#define AO_RADIO_LOTS 64
void
-ao_radio_send_lots(ao_radio_fill_func fill)
+ao_radio_send_aprs(ao_radio_fill_func fill)
{
uint8_t buf[AO_RADIO_LOTS], *b;
int cnt;
--- /dev/null
+/*
+ * Copyright © 2013 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 <ao.h>
+#include <ao_cc115l.h>
+#include <ao_exti.h>
+#include <ao_telemetry.h>
+#include <ao_fec.h>
+
+#define AO_RADIO_MAX_SEND sizeof (struct ao_telemetry_generic)
+
+uint8_t ao_radio_mutex;
+
+static uint8_t ao_radio_fifo; /* fifo drained interrupt received */
+static uint8_t ao_radio_done; /* tx done interrupt received */
+static uint8_t ao_radio_wake; /* sleep address for radio interrupts */
+static uint8_t ao_radio_abort; /* radio operation should abort */
+static uint8_t ao_radio_mcu_wake; /* MARC status change */
+static uint8_t ao_radio_marcstate; /* Last read MARC state value */
+
+/* Debugging commands */
+#define CC115L_DEBUG 0
+
+/* Runtime tracing */
+#define CC115L_TRACE 0
+
+#define FOSC 26000000
+
+#define ao_radio_select() ao_spi_get_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS,AO_SPI_SPEED_1MHz)
+#define ao_radio_deselect() ao_spi_put_mask(AO_CC115L_SPI_CS_PORT,(1 << AO_CC115L_SPI_CS_PIN),AO_CC115L_SPI_BUS)
+#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC115L_SPI_BUS)
+#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC115L_SPI_BUS)
+#define ao_radio_spi_recv(d,l) ao_spi_recv((d), (l), AO_CC115L_SPI_BUS)
+#define ao_radio_duplex(o,i,l) ao_spi_duplex((o), (i), (l), AO_CC115L_SPI_BUS)
+
+struct ao_cc115l_reg {
+ uint16_t addr;
+ char *name;
+};
+
+#if CC115L_TRACE
+
+const static struct ao_cc115l_reg ao_cc115l_reg[];
+const static char *cc115l_state_name[];
+
+enum ao_cc115l_trace_type {
+ trace_strobe,
+ trace_read,
+ trace_write,
+ trace_dma,
+ trace_line,
+};
+
+struct ao_cc115l_trace {
+ enum ao_cc115l_trace_type type;
+ int16_t addr;
+ int16_t value;
+ const char *comment;
+};
+
+#define NUM_TRACE 256
+
+static struct ao_cc115l_trace trace[NUM_TRACE];
+static int trace_i;
+static int trace_disable;
+
+static void trace_add(enum ao_cc115l_trace_type type, int16_t addr, int16_t value, const char *comment)
+{
+ if (trace_disable)
+ return;
+ switch (type) {
+ case trace_read:
+ case trace_write:
+ comment = ao_cc115l_reg[addr].name;
+ break;
+ case trace_strobe:
+ comment = cc115l_state_name[(value >> 4) & 0x7];
+ break;
+ }
+ trace[trace_i].type = type;
+ trace[trace_i].addr = addr;
+ trace[trace_i].value = value;
+ trace[trace_i].comment = comment;
+ if (++trace_i == NUM_TRACE)
+ trace_i = 0;
+}
+#else
+#define trace_add(t,a,v,c)
+#endif
+
+static uint8_t
+ao_radio_reg_read(uint8_t addr)
+{
+ uint8_t data[1];
+ uint8_t d;
+
+ data[0] = ((1 << CC115L_READ) |
+ (0 << CC115L_BURST) |
+ addr);
+ ao_radio_select();
+ ao_radio_spi_send(data, 1);
+ ao_radio_spi_recv(data, 1);
+ ao_radio_deselect();
+ trace_add(trace_read, addr, data[0], NULL);
+ return data[0];
+}
+
+static void
+ao_radio_reg_write(uint8_t addr, uint8_t value)
+{
+ uint8_t data[2];
+ uint8_t d;
+
+ trace_add(trace_write, addr, value, NULL);
+ data[0] = ((0 << CC115L_READ) |
+ (0 << CC115L_BURST) |
+ addr);
+ data[1] = value;
+ ao_radio_select();
+ ao_radio_spi_send(data, 2);
+ ao_radio_deselect();
+}
+
+static void
+ao_radio_burst_read_start (uint16_t addr)
+{
+ uint8_t data[1];
+ uint8_t d;
+
+ data[0] = ((1 << CC115L_READ) |
+ (1 << CC115L_BURST) |
+ addr);
+ ao_radio_select();
+ ao_radio_spi_send(data, 1);
+}
+
+static void
+ao_radio_burst_read_stop (void)
+{
+ ao_radio_deselect();
+}
+
+
+static uint8_t
+ao_radio_strobe(uint8_t addr)
+{
+ uint8_t in;
+
+ ao_radio_select();
+ ao_radio_duplex(&addr, &in, 1);
+ ao_radio_deselect();
+ trace_add(trace_strobe, addr, in, NULL);
+ return in;
+}
+
+static uint8_t
+ao_radio_fifo_write_start(void)
+{
+ uint8_t addr = ((0 << CC115L_READ) |
+ (1 << CC115L_BURST) |
+ CC115L_FIFO);
+ uint8_t status;
+
+ ao_radio_select();
+ ao_radio_duplex(&addr, &status, 1);
+ return status;
+}
+
+static inline uint8_t ao_radio_fifo_write_stop(uint8_t status) {
+ ao_radio_deselect();
+ return status;
+}
+
+static uint8_t
+ao_radio_fifo_write(uint8_t *data, uint8_t len)
+{
+ uint8_t status = ao_radio_fifo_write_start();
+ trace_add(trace_dma, CC115L_FIFO, len, NULL);
+ ao_radio_spi_send(data, len);
+ return ao_radio_fifo_write_stop(status);
+}
+
+static uint8_t
+ao_radio_tx_fifo_space(void)
+{
+ return CC115L_FIFO_SIZE - (ao_radio_reg_read(CC115L_TXBYTES) & CC115L_TXBYTES_NUM_TX_BYTES_MASK);
+}
+
+static uint8_t
+ao_radio_status(void)
+{
+ return ao_radio_strobe (CC115L_SNOP);
+}
+
+#define ao_radio_rdf_value 0x55
+
+static uint8_t
+ao_radio_get_marcstate(void)
+{
+ return ao_radio_reg_read(CC115L_MARCSTATE) & CC115L_MARCSTATE_MASK;
+}
+
+static void
+ao_radio_done_isr(void)
+{
+ ao_exti_disable(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN);
+ trace_add(trace_line, __LINE__, 0, "done_isr");
+ ao_radio_done = 1;
+ ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_fifo_isr(void)
+{
+ ao_exti_disable(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN);
+ trace_add(trace_line, __LINE__, 0, "fifo_isr");
+ ao_radio_fifo = 1;
+ ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_start_tx(void)
+{
+}
+
+static void
+ao_radio_idle(void)
+{
+ ao_radio_pa_off();
+ for (;;) {
+ uint8_t state = ao_radio_strobe(CC115L_SIDLE);
+ if ((state >> CC115L_STATUS_STATE) == CC115L_STATUS_STATE_IDLE)
+ break;
+ }
+ /* Flush any pending TX bytes */
+ ao_radio_strobe(CC115L_SFTX);
+}
+
+/*
+ * Packet deviation is 20.5kHz
+ *
+ * fdev = fosc >> 17 * (8 + dev_m) << dev_e
+ *
+ * 26e6 / (2 ** 17) * (8 + 5) * (2 ** 3) = 20630Hz
+ */
+
+#define PACKET_DEV_E 3
+#define PACKET_DEV_M 5
+
+/*
+ * For our packet data, set the symbol rate to 38400 Baud
+ *
+ * (256 + DATARATE_M) * 2 ** DATARATE_E
+ * Rdata = -------------------------------------- * fosc
+ * 2 ** 28
+ *
+ * (256 + 131) * (2 ** 10) / (2**28) * 26e6 = 38383
+ *
+ * DATARATE_M = 131
+ * DATARATE_E = 10
+ */
+#define PACKET_DRATE_E 10
+#define PACKET_DRATE_M 131
+
+static const uint16_t packet_setup[] = {
+ CC115L_DEVIATN, ((PACKET_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
+ (PACKET_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
+ CC115L_MDMCFG4, ((0xf << 4) |
+ (PACKET_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
+ CC115L_MDMCFG3, (PACKET_DRATE_M),
+ CC115L_MDMCFG2, (0x00 |
+ (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+ (0 << CC115L_MDMCFG2_MANCHESTER_EN) |
+ (CC115L_MDMCFG2_SYNC_MODE_16BITS << CC115L_MDMCFG2_SYNC_MODE)),
+};
+
+
+/*
+ * RDF deviation is 5kHz
+ *
+ * fdev = fosc >> 17 * (8 + dev_m) << dev_e
+ *
+ * 26e6 / (2 ** 17) * (8 + 4) * (2 ** 1) = 4761Hz
+ */
+
+#define RDF_DEV_E 1
+#define RDF_DEV_M 4
+
+/*
+ * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
+ *
+ * (256 + DATARATE_M) * 2 ** DATARATE_E
+ * Rdata = -------------------------------------- * fosc
+ * 2 ** 28
+ *
+ * (256 + 67) * (2 ** 6) / (2**28) * 26e6 = 2002
+ *
+ * DATARATE_M = 67
+ * DATARATE_E = 6
+ */
+#define RDF_DRATE_E 6
+#define RDF_DRATE_M 67
+
+static const uint16_t rdf_setup[] = {
+ CC115L_DEVIATN, ((RDF_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
+ (RDF_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
+ CC115L_MDMCFG4, ((0xf << 4) |
+ (RDF_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
+ CC115L_MDMCFG3, (RDF_DRATE_M),
+ CC115L_MDMCFG2, (0x00 |
+ (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+ (0 << CC115L_MDMCFG2_MANCHESTER_EN) |
+ (CC115L_MDMCFG2_SYNC_MODE_NONE << CC115L_MDMCFG2_SYNC_MODE)),
+};
+
+/*
+ * APRS deviation is the same as RDF
+ */
+
+#define APRS_DEV_E RDF_DEV_E
+#define APRS_DEV_M RDF_DEV_M
+
+/*
+ * For our APRS beacon, set the symbol rate to 9.6kBaud (8x oversampling for 1200 baud data rate)
+ *
+ * (256 + DATARATE_M) * 2 ** DATARATE_E
+ * Rdata = -------------------------------------- * fosc
+ * 2 ** 28
+ *
+ * (256 + 131) * (2 ** 8) / (2**28) * 26e6 = 9596
+ *
+ * DATARATE_M = 131
+ * DATARATE_E = 8
+ *
+ */
+#define APRS_DRATE_E 8
+#define APRS_DRATE_M 131
+
+static const uint16_t aprs_setup[] = {
+ CC115L_DEVIATN, ((APRS_DEV_E << CC115L_DEVIATN_DEVIATION_E) |
+ (APRS_DEV_M << CC115L_DEVIATN_DEVIATION_M)),
+ CC115L_MDMCFG4, ((0xf << 4) |
+ (APRS_DRATE_E << CC115L_MDMCFG4_DRATE_E)),
+ CC115L_MDMCFG3, (APRS_DRATE_M),
+ CC115L_MDMCFG2, (0x00 |
+ (CC115L_MDMCFG2_MOD_FORMAT_GFSK << CC115L_MDMCFG2_MOD_FORMAT) |
+ (0 << CC115L_MDMCFG2_MANCHESTER_EN) |
+ (CC115L_MDMCFG2_SYNC_MODE_NONE << CC115L_MDMCFG2_SYNC_MODE)),
+};
+
+#define AO_PKTCTRL0_INFINITE ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \
+ (0 << CC115L_PKTCTRL0_PKT_CRC_EN) | \
+ (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_INFINITE << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG))
+#define AO_PKTCTRL0_FIXED ((CC115L_PKTCTRL0_PKT_FORMAT_NORMAL << CC115L_PKTCTRL0_PKT_FORMAT) | \
+ (0 << CC115L_PKTCTRL0_PKT_CRC_EN) | \
+ (CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_FIXED << CC115L_PKTCTRL0_PKT_LENGTH_CONFIG))
+
+static uint16_t ao_radio_mode;
+
+
+/*
+ * These set the data rate and modulation parameters
+ */
+#define AO_RADIO_MODE_BITS_PACKET_TX 1
+#define AO_RADIO_MODE_BITS_RDF 2
+#define AO_RADIO_MODE_BITS_APRS 4
+
+/*
+ * Flips between infinite packet mode and fixed packet mode;
+ * we use infinite mode until the sender gives us the
+ * last chunk of data
+ */
+#define AO_RADIO_MODE_BITS_INFINITE 40
+#define AO_RADIO_MODE_BITS_FIXED 80
+
+#define AO_RADIO_MODE_NONE 0
+
+#define AO_RADIO_MODE_RDF AO_RADIO_MODE_BITS_RDF
+#define AO_RADIO_MODE_PACKET_TX AO_RADIO_MODE_BITS_PACKET_TX
+#define AO_RADIO_MODE_APRS AO_RADIO_MODE_BITS_APRS
+
+static void
+ao_radio_set_mode(uint16_t new_mode)
+{
+ uint16_t changes;
+ int i;
+
+ if (new_mode == ao_radio_mode)
+ return;
+
+ changes = new_mode & (~ao_radio_mode);
+ if (changes & AO_RADIO_MODE_BITS_PACKET_TX)
+ for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2)
+ ao_radio_reg_write(packet_setup[i], packet_setup[i+1]);
+
+ if (changes & AO_RADIO_MODE_BITS_RDF)
+ for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2)
+ ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]);
+
+ if (changes & AO_RADIO_MODE_BITS_APRS)
+ for (i = 0; i < sizeof (aprs_setup) / sizeof (aprs_setup[0]); i += 2)
+ ao_radio_reg_write(aprs_setup[i], aprs_setup[i+1]);
+
+ if (changes & AO_RADIO_MODE_BITS_INFINITE)
+ ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_INFINITE);
+
+ if (changes & AO_RADIO_MODE_BITS_FIXED)
+ ao_radio_reg_write(CC115L_PKTCTRL0, AO_PKTCTRL0_FIXED);
+
+ ao_radio_mode = new_mode;
+}
+
+/***************************************************************
+ * SmartRF Studio(tm) Export
+ *
+ * Radio register settings specifed with address, value
+ *
+ * RF device: CC115L
+ *
+ ***************************************************************/
+
+static const uint16_t radio_setup[] = {
+
+ /* High when FIFO is above threshold, low when fifo is below threshold */
+ AO_CC115L_FIFO_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_TXFIFO_THR,
+
+ /* High when transmitter is running, low when off */
+ AO_CC115L_DONE_INT_GPIO_IOCFG, CC115L_IOCFG_GPIO_CFG_PA_PD | (1 << CC115L_IOCFG_GPIO_INV),
+
+ CC115L_FIFOTHR, 0x47, /* TX FIFO Thresholds */
+ CC115L_MDMCFG1, (0x00 |
+ (CC115L_MDMCFG1_NUM_PREAMBLE_4 << CC115L_MDMCFG1_NUM_PREAMBLE) |
+ (1 << CC115L_MDMCFG1_CHANSPC_E)),
+ CC115L_MDMCFG0, 248, /* Channel spacing M value (100kHz channels) */
+ CC115L_MCSM0, 0x38, /* Main Radio Control State Machine Configuration */
+ CC115L_RESERVED_0X20, 0xfb, /* Use setting from SmartRF Studio */
+ CC115L_FSCAL3, 0xe9, /* Frequency Synthesizer Calibration */
+ CC115L_FSCAL2, 0x2a, /* Frequency Synthesizer Calibration */
+ CC115L_FSCAL1, 0x00, /* Frequency Synthesizer Calibration */
+ CC115L_FSCAL0, 0x1f, /* Frequency Synthesizer Calibration */
+ CC115L_TEST2, 0x81, /* Various Test Settings */
+ CC115L_TEST1, 0x35, /* Various Test Settings */
+ CC115L_TEST0, 0x09, /* Various Test Settings */
+};
+
+static uint8_t ao_radio_configured = 0;
+
+static void
+ao_radio_setup(void)
+{
+ int i;
+
+ ao_radio_strobe(CC115L_SRES);
+ ao_delay(AO_MS_TO_TICKS(10));
+
+ for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2)
+ ao_radio_reg_write(radio_setup[i], radio_setup[i+1]);
+
+ ao_radio_mode = 0;
+
+ ao_config_get();
+
+ ao_radio_configured = 1;
+}
+
+static void
+ao_radio_set_len(uint8_t len)
+{
+ static uint8_t last_len;
+
+ if (len != last_len) {
+ ao_radio_reg_write(CC115L_PKTLEN, len);
+ last_len = len;
+ }
+}
+
+static void
+ao_radio_get(void)
+{
+ static uint32_t last_radio_setting;
+ static uint8_t last_power_setting;
+
+ ao_mutex_get(&ao_radio_mutex);
+ if (!ao_radio_configured)
+ ao_radio_setup();
+ if (ao_config.radio_setting != last_radio_setting) {
+ ao_radio_reg_write(CC115L_FREQ2, ao_config.radio_setting >> 16);
+ ao_radio_reg_write(CC115L_FREQ1, ao_config.radio_setting >> 8);
+ ao_radio_reg_write(CC115L_FREQ0, ao_config.radio_setting);
+ last_radio_setting = ao_config.radio_setting;
+ }
+ if (ao_config.radio_power != last_power_setting) {
+ ao_radio_reg_write(CC115L_PA, ao_config.radio_power);
+ last_power_setting = ao_config.radio_power;
+ }
+}
+
+static void
+_ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode);
+
+#define ao_radio_put() ao_mutex_put(&ao_radio_mutex)
+
+struct ao_radio_tone {
+ uint8_t value;
+ uint8_t len;
+};
+
+struct ao_radio_tone *ao_radio_tone;
+uint8_t ao_radio_tone_count;
+uint8_t ao_radio_tone_current;
+uint8_t ao_radio_tone_offset;
+
+int16_t
+ao_radio_tone_fill(uint8_t *buf, int16_t len)
+{
+ int16_t ret = 0;
+
+ while (len) {
+ int16_t this_time;
+ struct ao_radio_tone *t;
+
+ /* Figure out how many to send of the current value */
+ t = &ao_radio_tone[ao_radio_tone_current];
+ this_time = t->len - ao_radio_tone_offset;
+ if (this_time > len)
+ this_time = len;
+
+ /* queue the data */
+ memset(buf, t->value, this_time);
+
+ /* mark as sent */
+ len -= this_time;
+ ao_radio_tone_offset += this_time;
+ ret += this_time;
+
+ if (ao_radio_tone_offset >= t->len) {
+ ao_radio_tone_offset = 0;
+ ao_radio_tone_current++;
+ if (ao_radio_tone_current >= ao_radio_tone_count) {
+ trace_add(trace_line, __LINE__, ret, "done with tone");
+ return -ret;
+ }
+ }
+ }
+ trace_add(trace_line, __LINE__, ret, "got some tone");
+ return ret;
+}
+
+static void
+ao_radio_tone_run(struct ao_radio_tone *tones, int ntones)
+{
+ ao_radio_get();
+ ao_radio_tone = tones;
+ ao_radio_tone_current = 0;
+ ao_radio_tone_offset = 0;
+ _ao_radio_send_lots(ao_radio_tone_fill, AO_RADIO_MODE_RDF);
+ ao_radio_put();
+}
+
+void
+ao_radio_rdf(void)
+{
+ struct ao_radio_tone tone;
+
+ tone.value = ao_radio_rdf_value;
+ tone.len = AO_RADIO_RDF_LEN;
+ ao_radio_tone_run(&tone, 1);
+}
+
+void
+ao_radio_continuity(uint8_t c)
+{
+ struct ao_radio_tone tones[7];
+ uint8_t count = 0;
+ uint8_t i;
+
+ for (i = 0; i < 3; i++) {
+ tones[count].value = 0x00;
+ tones[count].len = AO_RADIO_CONT_PAUSE_LEN;
+ count++;
+ if (i < c)
+ tones[count].value = ao_radio_rdf_value;
+ else
+ tones[count].value = 0x00;
+ tones[count].len = AO_RADIO_CONT_TONE_LEN;
+ count++;
+ }
+ tones[count].value = 0x00;
+ tones[count].len = AO_RADIO_CONT_PAUSE_LEN;
+ count++;
+ ao_radio_tone_run(tones, count);
+}
+
+void
+ao_radio_rdf_abort(void)
+{
+ ao_radio_abort = 1;
+ ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_test_cmd(void)
+{
+ uint8_t mode = 2;
+ static uint8_t radio_on;
+ ao_cmd_white();
+ if (ao_cmd_lex_c != '\n') {
+ ao_cmd_decimal();
+ mode = (uint8_t) ao_cmd_lex_u32;
+ }
+ mode++;
+ if ((mode & 2) && !radio_on) {
+#if HAS_MONITOR
+ ao_monitor_disable();
+#endif
+#if PACKET_HAS_SLAVE
+ ao_packet_slave_stop();
+#endif
+ ao_radio_get();
+ ao_radio_set_len(0xff);
+ ao_radio_set_mode(AO_RADIO_MODE_RDF|AO_RADIO_MODE_BITS_FIXED);
+ ao_radio_strobe(CC115L_SFTX);
+ ao_radio_pa_on();
+ ao_radio_strobe(CC115L_STX);
+ radio_on = 1;
+ }
+ if (mode == 3) {
+ printf ("Hit a character to stop..."); flush();
+ getchar();
+ putchar('\n');
+ }
+ if ((mode & 1) && radio_on) {
+ ao_radio_idle();
+ ao_radio_put();
+ radio_on = 0;
+#if HAS_MONITOR
+ ao_monitor_enable();
+#endif
+ }
+}
+
+static inline int16_t
+ao_radio_gpio_bits(void)
+{
+ return AO_CC115L_DONE_INT_PORT->idr & ((1 << AO_CC115L_FIFO_INT_PIN) |
+ (1 << AO_CC115L_DONE_INT_PIN));
+}
+
+static void
+ao_radio_wait_fifo(void)
+{
+ ao_arch_block_interrupts();
+ while (!ao_radio_fifo && !ao_radio_done && !ao_radio_abort) {
+ trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wait_fifo");
+ ao_sleep(&ao_radio_wake);
+ }
+ ao_arch_release_interrupts();
+ trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wake bits");
+ trace_add(trace_line, __LINE__, ao_radio_fifo, "wake fifo");
+ trace_add(trace_line, __LINE__, ao_radio_done, "wake done");
+ trace_add(trace_line, __LINE__, ao_radio_abort, "wake abort");
+}
+
+static void
+ao_radio_wait_done(void)
+{
+ ao_arch_block_interrupts();
+ while (!ao_radio_done && !ao_radio_abort) {
+ trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wait_done");
+ ao_sleep(&ao_radio_wake);
+ }
+ ao_arch_release_interrupts();
+ trace_add(trace_line, __LINE__, ao_radio_gpio_bits(), "wake bits");
+ trace_add(trace_line, __LINE__, ao_radio_fifo, "wake fifo");
+ trace_add(trace_line, __LINE__, ao_radio_done, "wake done");
+ trace_add(trace_line, __LINE__, ao_radio_abort, "wake abort");
+}
+
+static uint8_t tx_data[(AO_RADIO_MAX_SEND + 4) * 2];
+
+static uint8_t *ao_radio_send_buf;
+static int16_t ao_radio_send_len;
+
+static int16_t
+ao_radio_send_fill(uint8_t *buf, int16_t len)
+{
+ int16_t this_time;
+
+ this_time = ao_radio_send_len;
+ if (this_time > len)
+ this_time = len;
+ memcpy(buf, ao_radio_send_buf, this_time);
+ ao_radio_send_buf += this_time;
+ ao_radio_send_len -= this_time;
+ if (ao_radio_send_len == 0)
+ return -this_time;
+ return this_time;
+}
+
+void
+ao_radio_send(const void *d, uint8_t size)
+{
+ int i;
+
+ ao_radio_get();
+ ao_radio_send_len = ao_fec_encode(d, size, tx_data);
+ ao_radio_send_buf = tx_data;
+ _ao_radio_send_lots(ao_radio_send_fill, AO_RADIO_MODE_PACKET_TX);
+ ao_radio_put();
+}
+
+#define AO_RADIO_LOTS 64
+
+static void
+_ao_radio_send_lots(ao_radio_fill_func fill, uint8_t mode)
+{
+ uint8_t buf[AO_RADIO_LOTS], *b;
+ int cnt;
+ int total = 0;
+ uint8_t done = 0;
+ uint8_t started = 0;
+ uint8_t fifo_space;
+
+ fifo_space = CC115L_FIFO_SIZE;
+ ao_radio_done = 0;
+ ao_radio_fifo = 0;
+ while (!done) {
+ cnt = (*fill)(buf, sizeof(buf));
+ trace_add(trace_line, __LINE__, cnt, "send data count");
+ if (cnt < 0) {
+ done = 1;
+ cnt = -cnt;
+ }
+ total += cnt;
+
+ /* At the last buffer, set the total length */
+ if (done) {
+ ao_radio_set_len(total & 0xff);
+ ao_radio_set_mode(mode | AO_RADIO_MODE_BITS_FIXED);
+ } else {
+ ao_radio_set_len(0xff);
+ ao_radio_set_mode(mode | AO_RADIO_MODE_BITS_INFINITE);
+ }
+
+ b = buf;
+ while (cnt) {
+ uint8_t this_len = cnt;
+
+ /* Wait for some space in the fifo */
+ while (!ao_radio_abort && (fifo_space = ao_radio_tx_fifo_space()) == 0) {
+ trace_add(trace_line, __LINE__, this_len, "wait for space");
+ ao_radio_wait_fifo();
+ }
+ if (ao_radio_abort || ao_radio_done)
+ break;
+ trace_add(trace_line, __LINE__, fifo_space, "got space");
+ if (this_len > fifo_space)
+ this_len = fifo_space;
+
+ cnt -= this_len;
+
+ ao_radio_done = 0;
+ ao_radio_fifo = 0;
+ ao_radio_fifo_write(b, this_len);
+ b += this_len;
+
+ ao_exti_enable(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN);
+ ao_exti_enable(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN);
+
+ if (!started) {
+ ao_radio_pa_on();
+ ao_radio_strobe(CC115L_STX);
+ started = 1;
+ }
+ }
+ if (ao_radio_abort || ao_radio_done)
+ break;
+ }
+ if (ao_radio_abort)
+ ao_radio_idle();
+ ao_radio_wait_done();
+ ao_radio_pa_off();
+}
+
+void
+ao_radio_send_aprs(ao_radio_fill_func fill)
+{
+ ao_radio_get();
+ _ao_radio_send_lots(fill, AO_RADIO_MODE_APRS);
+ ao_radio_put();
+}
+
+#if CC115L_DEBUG
+const static char *cc115l_state_name[] = {
+ [CC115L_STATUS_STATE_IDLE] = "IDLE",
+ [CC115L_STATUS_STATE_TX] = "TX",
+ [CC115L_STATUS_STATE_FSTXON] = "FSTXON",
+ [CC115L_STATUS_STATE_CALIBRATE] = "CALIBRATE",
+ [CC115L_STATUS_STATE_SETTLING] = "SETTLING",
+ [CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW] = "TX_FIFO_UNDERFLOW",
+};
+
+const static struct ao_cc115l_reg ao_cc115l_reg[] = {
+ { .addr = CC115L_IOCFG2, .name = "IOCFG2" },
+ { .addr = CC115L_IOCFG1, .name = "IOCFG1" },
+ { .addr = CC115L_IOCFG0, .name = "IOCFG0" },
+ { .addr = CC115L_FIFOTHR, .name = "FIFOTHR" },
+ { .addr = CC115L_SYNC1, .name = "SYNC1" },
+ { .addr = CC115L_SYNC0, .name = "SYNC0" },
+ { .addr = CC115L_PKTLEN, .name = "PKTLEN" },
+ { .addr = CC115L_PKTCTRL0, .name = "PKTCTRL0" },
+ { .addr = CC115L_CHANNR, .name = "CHANNR" },
+ { .addr = CC115L_FSCTRL0, .name = "FSCTRL0" },
+ { .addr = CC115L_FREQ2, .name = "FREQ2" },
+ { .addr = CC115L_FREQ1, .name = "FREQ1" },
+ { .addr = CC115L_FREQ0, .name = "FREQ0" },
+ { .addr = CC115L_MDMCFG4, .name = "MDMCFG4" },
+ { .addr = CC115L_MDMCFG3, .name = "MDMCFG3" },
+ { .addr = CC115L_MDMCFG2, .name = "MDMCFG2" },
+ { .addr = CC115L_MDMCFG1, .name = "MDMCFG1" },
+ { .addr = CC115L_MDMCFG0, .name = "MDMCFG0" },
+ { .addr = CC115L_DEVIATN, .name = "DEVIATN" },
+ { .addr = CC115L_MCSM1, .name = "MCSM1" },
+ { .addr = CC115L_MCSM0, .name = "MCSM0" },
+ { .addr = CC115L_RESERVED_0X20, .name = "RESERVED_0X20" },
+ { .addr = CC115L_FREND0, .name = "FREND0" },
+ { .addr = CC115L_FSCAL3, .name = "FSCAL3" },
+ { .addr = CC115L_FSCAL2, .name = "FSCAL2" },
+ { .addr = CC115L_FSCAL1, .name = "FSCAL1" },
+ { .addr = CC115L_FSCAL0, .name = "FSCAL0" },
+ { .addr = CC115L_RESERVED_0X29, .name = "RESERVED_0X29" },
+ { .addr = CC115L_RESERVED_0X2A, .name = "RESERVED_0X2A" },
+ { .addr = CC115L_RESERVED_0X2B, .name = "RESERVED_0X2B" },
+ { .addr = CC115L_TEST2, .name = "TEST2" },
+ { .addr = CC115L_TEST1, .name = "TEST1" },
+ { .addr = CC115L_TEST0, .name = "TEST0" },
+ { .addr = CC115L_PARTNUM, .name = "PARTNUM" },
+ { .addr = CC115L_VERSION, .name = "VERSION" },
+ { .addr = CC115L_MARCSTATE, .name = "MARCSTATE" },
+ { .addr = CC115L_PKTSTATUS, .name = "PKTSTATUS" },
+ { .addr = CC115L_TXBYTES, .name = "TXBYTES" },
+ { .addr = CC115L_PA, .name = "PA" },
+};
+
+#define AO_NUM_CC115L_REG (sizeof ao_cc115l_reg / sizeof ao_cc115l_reg[0])
+
+static void ao_radio_show(void) {
+ uint8_t status = ao_radio_status();
+ int i;
+
+ ao_radio_get();
+ status = ao_radio_status();
+ printf ("Status: %02x\n", status);
+ printf ("CHIP_RDY: %d\n", (status >> CC115L_STATUS_CHIP_RDY) & 1);
+ printf ("STATE: %s\n", cc115l_state_name[(status >> CC115L_STATUS_STATE) & CC115L_STATUS_STATE_MASK]);
+ printf ("MARC: %02x\n", ao_radio_get_marcstate());
+
+ for (i = 0; i < AO_NUM_CC115L_REG; i++)
+ printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc115l_reg[i].addr), ao_cc115l_reg[i].name);
+ ao_radio_put();
+}
+
+static void ao_radio_beep(void) {
+ ao_radio_rdf();
+}
+
+static void ao_radio_packet(void) {
+ static const uint8_t packet[] = {
+#if 1
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+#else
+ 3, 1, 2, 3
+#endif
+ };
+
+ ao_radio_send(packet, sizeof (packet));
+}
+
+#endif /* CC115L_DEBUG */
+
+#if HAS_APRS
+#include <ao_aprs.h>
+
+static void
+ao_radio_aprs()
+{
+#if PACKET_HAS_SLAVE
+ ao_packet_slave_stop();
+#endif
+ ao_aprs_send();
+}
+#endif
+
+static const struct ao_cmds ao_radio_cmds[] = {
+ { ao_radio_test_cmd, "C <1 start, 0 stop, none both>\0Radio carrier test" },
+#if CC115L_DEBUG
+#if HAS_APRS
+ { ao_radio_aprs, "G\0Send APRS packet" },
+#endif
+ { ao_radio_show, "R\0Show CC115L status" },
+ { ao_radio_beep, "b\0Emit an RDF beacon" },
+ { ao_radio_packet, "p\0Send a test packet" },
+#endif
+ { 0, NULL }
+};
+
+void
+ao_radio_init(void)
+{
+ int i;
+
+ ao_radio_configured = 0;
+ ao_spi_init_cs (AO_CC115L_SPI_CS_PORT, (1 << AO_CC115L_SPI_CS_PIN));
+
+#if 0
+ AO_CC115L_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC115L_SPI_CS_PIN));
+ for (i = 0; i < 10000; i++) {
+ if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0)
+ break;
+ }
+ AO_CC115L_SPI_CS_PORT->bsrr = (1 << AO_CC115L_SPI_CS_PIN);
+ if (i == 10000)
+ ao_panic(AO_PANIC_SELF_TEST_CC115L);
+#endif
+
+ /* Enable the fifo threhold interrupt pin */
+ ao_enable_port(AO_CC115L_FIFO_INT_PORT);
+ ao_exti_setup(AO_CC115L_FIFO_INT_PORT, AO_CC115L_FIFO_INT_PIN,
+ AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
+ ao_radio_fifo_isr);
+
+ /* Enable the tx done interrupt pin */
+ ao_enable_port(AO_CC115L_DONE_INT_PORT);
+ ao_exti_setup(AO_CC115L_DONE_INT_PORT, AO_CC115L_DONE_INT_PIN,
+ AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED,
+ ao_radio_done_isr);
+
+ ao_radio_pa_init();
+
+ ao_cmd_register(&ao_radio_cmds[0]);
+}
--- /dev/null
+/*
+ * Copyright © 2013 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.
+ */
+
+#ifndef _AO_CC115L_H_
+#define _AO_CC115L_H_
+
+#define CC115L_BURST 6
+#define CC115L_READ 7
+
+/* Register space */
+#define CC115L_IOCFG2 0x00 /* GDO2 Output Pin Configuration */
+#define CC115L_IOCFG1 0x01 /* GDO1 Output Pin Configuration */
+#define CC115L_IOCFG0 0x02 /* GDO0 Output Pin Configuration */
+
+#define CC115L_IOCFG_GPIO1_DS 7
+#define CC115L_IOCFG_GPIO_INV 6
+
+#define CC115L_IOCFG_GPIO_CFG 0
+#define CC115L_IOCFG_GPIO_CFG_TXFIFO_THR 2
+#define CC115L_IOCFG_GPIO_CFG_TXFIFO_THR_PKT 3
+#define CC115L_IOCFG_GPIO_CFG_TXFIFO_UNDERFLOW 5
+#define CC115L_IOCFG_GPIO_CFG_PKT_SYNC_TX 6
+#define CC115L_IOCFG_GPIO_CFG_PLL_LOCKED 10
+#define CC115L_IOCFG_GPIO_CFG_SERIAL_CLK 11
+#define CC115L_IOCFG_GPIO_CFG_SYNC_DATA 12
+#define CC115L_IOCFG_GPIO_CFG_ASYNC_DATA 13
+#define CC115L_IOCFG_GPIO_CFG_PA_PD 27
+#define CC115L_IOCFG_GPIO_CFG_CHIP_RDYn 41
+#define CC115L_IOCFG_GPIO_CFG_XOSC_STABLE 43
+#define CC115L_IOCFG_GPIO_CFG_HIGHZ 46
+#define CC115L_IOCFG_GPIO_CFG_HW_0 47
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_1 48
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_1_5 49
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_2 50
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_3 51
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_4 52
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_6 53
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_8 54
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_12 55
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_16 56
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_24 57
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_32 58
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_48 59
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_64 60
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_96 61
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_128 62
+#define CC115L_IOCFG_GPIO_CFG_CLK_XOSC_192 63
+#define CC115L_IOCFG_GPIO_CFG_MASK 0x3f
+
+#define CC115L_FIFOTHR 0x03 /* TX FIFO Thresholds */
+#define CC115L_FIFOTHR_THR_MASK 0x0f
+#define CC115L_FIFOTHR_THR_61 0
+#define CC115L_FIFOTHR_THR_57 1
+#define CC115L_FIFOTHR_THR_53 2
+#define CC115L_FIFOTHR_THR_49 3
+#define CC115L_FIFOTHR_THR_45 4
+#define CC115L_FIFOTHR_THR_41 5
+#define CC115L_FIFOTHR_THR_37 6
+#define CC115L_FIFOTHR_THR_33 7
+#define CC115L_FIFOTHR_THR_29 8
+#define CC115L_FIFOTHR_THR_25 9
+#define CC115L_FIFOTHR_THR_21 10
+#define CC115L_FIFOTHR_THR_17 11
+#define CC115L_FIFOTHR_THR_13 12
+#define CC115L_FIFOTHR_THR_9 13
+#define CC115L_FIFOTHR_THR_5 14
+#define CC115L_FIFOTHR_THR_1 15
+
+#define CC115L_SYNC1 0x04 /* Sync Word, High Byte */
+#define CC115L_SYNC0 0x05 /* Sync Word, Low Byte */
+#define CC115L_PKTLEN 0x06 /* Packet Length */
+#define CC115L_PKTCTRL0 0x08 /* Packet Automation Control */
+#define CC115L_PKTCTRL0_PKT_FORMAT 4
+#define CC115L_PKTCTRL0_PKT_FORMAT_NORMAL 0
+#define CC115L_PKTCTRL0_PKT_FORMAT_SYNC_SERIAL 1
+#define CC115L_PKTCTRL0_PKT_FORMAT_RANDOM 2
+#define CC115L_PKTCTRL0_PKT_FORMAT_ASYNC_SERIAL 3
+#define CC115L_PKTCTRL0_PKT_FORMAT_MASK 3
+#define CC115L_PKTCTRL0_PKT_CRC_EN 2
+#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG 0
+#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_FIXED 0
+#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_VARIABLE 1
+#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_INFINITE 2
+#define CC115L_PKTCTRL0_PKT_LENGTH_CONFIG_MASK 3
+#define CC115L_CHANNR 0x0a /* Channel Number */
+#define CC115L_FSCTRL0 0x0c /* Frequency Synthesizer Control */
+#define CC115L_FREQ2 0x0d /* Frequency Control Word, High Byte */
+#define CC115L_FREQ1 0x0e /* Frequency Control Word, Middle Byte */
+#define CC115L_FREQ0 0x0f /* Frequency Control Word, Low Byte */
+#define CC115L_MDMCFG4 0x10 /* Modem Configuration */
+#define CC115L_MDMCFG4_DRATE_E 0
+#define CC115L_MDMCFG3 0x11 /* Modem Configuration */
+#define CC115L_MDMCFG2 0x12 /* Modem Configuration */
+#define CC115L_MDMCFG2_MOD_FORMAT 4
+#define CC115L_MDMCFG2_MOD_FORMAT_2FSK 0
+#define CC115L_MDMCFG2_MOD_FORMAT_GFSK 1
+#define CC115L_MDMCFG2_MOD_FORMAT_OOK 3
+#define CC115L_MDMCFG2_MOD_FORMAT_4FSK 4
+#define CC115L_MDMCFG2_MOD_FORMAT_MASK 7
+#define CC115L_MDMCFG2_MANCHESTER_EN 3
+#define CC115L_MDMCFG2_SYNC_MODE 0
+#define CC115L_MDMCFG2_SYNC_MODE_NONE 0
+#define CC115L_MDMCFG2_SYNC_MODE_16BITS 1
+#define CC115L_MDMCFG2_SYNC_MODE_32BITS 3
+#define CC115L_MDMCFG2_SYNC_MODE_MASK 3
+#define CC115L_MDMCFG1 0x13 /* Modem Configuration */
+#define CC115L_MDMCFG1_NUM_PREAMBLE 4
+#define CC115L_MDMCFG1_NUM_PREAMBLE_2 0
+#define CC115L_MDMCFG1_NUM_PREAMBLE_3 1
+#define CC115L_MDMCFG1_NUM_PREAMBLE_4 2
+#define CC115L_MDMCFG1_NUM_PREAMBLE_6 3
+#define CC115L_MDMCFG1_NUM_PREAMBLE_8 4
+#define CC115L_MDMCFG1_NUM_PREAMBLE_12 5
+#define CC115L_MDMCFG1_NUM_PREAMBLE_16 6
+#define CC115L_MDMCFG1_NUM_PREAMBLE_24 7
+#define CC115L_MDMCFG1_NUM_PREAMBLE_MASK 7
+#define CC115L_MDMCFG1_CHANSPC_E 0
+#define CC115L_MDMCFG0 0x14 /* Modem Configuration */
+#define CC115L_DEVIATN 0x15 /* Modem Deviation Setting */
+#define CC115L_DEVIATN_DEVIATION_E 4
+#define CC115L_DEVIATN_DEVIATION_E_MASK 7
+#define CC115L_DEVIATN_DEVIATION_M 0
+#define CC115L_DEVIATN_DEVIATION_M_MASK 7
+#define CC115L_MCSM1 0x17 /* Main Radio Control State Machine Configuration */
+#define CC115L_MCSM1_TXOFF_MODE 0
+#define CC115L_MCSM1_TXOFF_MODE_IDLE 0
+#define CC115L_MCSM1_TXOFF_MODE_FSTXON 1
+#define CC115L_MCSM1_TXOFF_MODE_TX 2
+#define CC115L_MCSM1_TXOFF_MODE_MASK 3
+#define CC115L_MCSM0 0x18 /* Main Radio Control State Machine Configuration */
+#define CC115L_MCSM0_FS_AUTOCAL 4
+#define CC115L_MCSM0_FS_AUTOCAL_NEVER 0
+#define CC115L_MCSM0_FS_AUTOCAL_IDLE_TO_TX 1
+#define CC115L_MCSM0_FS_AUTOCAL_TX_TO_IDLE 2
+#define CC115L_MCSM0_FS_AUTOCAL_4TH_TX_TO_IDLE 3
+#define CC115L_MCSM0_FS_AUTOCAL_MASK 3
+#define CC115L_MCSM0_PO_TIMEOUT 2
+#define CC115L_MCSM0_PO_TIMEOUT_1 0
+#define CC115L_MCSM0_PO_TIMEOUT_16 1
+#define CC115L_MCSM0_PO_TIMEOUT_64 2
+#define CC115L_MCSM0_PO_TIMEOUT_256 3
+#define CC115L_MCSM0_PO_TIMEOUT_MASK 3
+#define CC115L_MCSM0_XOSC_FORCE_ON 0
+#define CC115L_RESERVED_0X20 0x20 /* Use setting from SmartRF Studio */
+#define CC115L_FREND0 0x22 /* Front End TX Configuration */
+#define CC115L_FSCAL3 0x23 /* Frequency Synthesizer Calibration */
+#define CC115L_FSCAL2 0x24 /* Frequency Synthesizer Calibration */
+#define CC115L_FSCAL1 0x25 /* Frequency Synthesizer Calibration */
+#define CC115L_FSCAL0 0x26 /* Frequency Synthesizer Calibration */
+#define CC115L_RESERVED_0X29 0x29 /* Use setting from SmartRF Studio */
+#define CC115L_RESERVED_0X2A 0x2a /* Use setting from SmartRF Studio */
+#define CC115L_RESERVED_0X2B 0x2b /* Use setting from SmartRF Studio */
+#define CC115L_TEST2 0x2c /* Various Test Settings */
+#define CC115L_TEST1 0x2d /* Various Test Settings */
+#define CC115L_TEST0 0x2e /* Various Test Settings */
+
+/* Status registers (use BURST bit to select these) */
+#define CC115L_PARTNUM (0x30|(1<<CC115L_BURST)) /* Part number for CC115L */
+#define CC115L_VERSION (0x31|(1<<CC115L_BURST)) /* Current version number */
+#define CC115L_MARCSTATE (0x35|(1<<CC115L_BURST)) /* Control state machine state */
+#define CC115L_MARCSTATE_MASK 0x1f
+#define CC115L_MARCSTATE_SLEEP 0x00
+#define CC115L_MARCSTATE_IDLE 0x01
+#define CC115L_MARCSTATE_XOFF 0x02
+#define CC115L_MARCSTATE_VCOON_MC 0x03
+#define CC115L_MARCSTATE_REGON_MC 0x04
+#define CC115L_MARCSTATE_MANCAL 0x05
+#define CC115L_MARCSTATE_VCOON 0x06
+#define CC115L_MARCSTATE_REGON 0x07
+#define CC115L_MARCSTATE_STARTCAL 0x08
+#define CC115L_MARCSTATE_BWBOOST 0x09
+#define CC115L_MARCSTATE_FS_LOCK 0x0a
+#define CC115L_MARCSTATE_ENDCAL 0x0c
+#define CC115L_MARCSTATE_FSTXON 0x12
+#define CC115L_MARCSTATE_TX 0x13
+#define CC115L_MARCSTATE_TX_END 0x14
+#define CC115L_MARCSTATE_TX_UNDERFLOW 0x16
+#define CC115L_PKTSTATUS (0x38|(1<<CC115L_BURST)) /* Current GDOx status and packet status */
+#define CC115L_TXBYTES (0x3a|(1<<CC115L_BURST)) /* Underflow and number of bytes in the TX FIFO */
+#define CC115L_TXBYTES_TXFIFO_UNDERFLOW 7
+#define CC115L_TXBYTES_NUM_TX_BYTES 0
+#define CC115L_TXBYTES_NUM_TX_BYTES_MASK 0x7f
+
+/* Command strobes (no BURST bit for these) */
+#define CC115L_SRES 0x30
+#define CC115L_SFSTXON 0x31
+#define CC115L_SXOFF 0x32
+#define CC115L_SCAL 0x33
+#define CC115L_STX 0x35
+#define CC115L_SIDLE 0x36
+#define CC115L_SPWD 0x39
+#define CC115L_SFTX 0x3b
+#define CC115L_SNOP 0x3d
+
+#define CC115L_PA 0x3e
+#define CC115L_FIFO 0x3f
+
+#define CC115L_FIFO_SIZE 64
+
+/* Status byte */
+#define CC115L_STATUS_CHIP_RDY 7
+#define CC115L_STATUS_STATE 4
+#define CC115L_STATUS_STATE_IDLE 0
+#define CC115L_STATUS_STATE_TX 2
+#define CC115L_STATUS_STATE_FSTXON 3
+#define CC115L_STATUS_STATE_CALIBRATE 4
+#define CC115L_STATUS_STATE_SETTLING 5
+#define CC115L_STATUS_STATE_TX_FIFO_UNDERFLOW 7
+#define CC115L_STATUS_STATE_MASK 7
+
+#define CC115L_STATUS_FIFO_BYTES_AVAILABLE 0
+#endif /* _AO_CC115L_H_ */
#include <ao.h>
#include <ao_companion.h>
-#ifdef MEGAMETRUM
+#ifdef TELEMEGA
#define ao_spi_slow(b)
#define ao_spi_fast(b)
#endif
--- /dev/null
+/*
+ * Copyright © 2013 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.
+ */
+
+#ifndef AO_FAT_TEST
+#include "ao.h"
+#endif
+
+#include "ao_fat.h"
+#include "ao_bufio.h"
+
+/* Include FAT commands */
+#ifndef AO_FAT_TEST
+#define FAT_COMMANDS 1
+#endif
+
+/* Spew FAT tracing */
+#define FAT_TRACE 0
+
+#ifdef DBG
+#undef DBG
+#endif
+
+#if FAT_TRACE
+#define DBG(...) printf(__VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+/*
+ * Basic file system types
+ */
+
+typedef ao_fat_offset_t offset_t;
+typedef ao_fat_sector_t sector_t;
+typedef ao_fat_cluster_t cluster_t;
+typedef ao_fat_dirent_t dirent_t;
+typedef ao_fat_cluster_offset_t cluster_offset_t;
+
+/* Global FAT lock */
+static uint8_t ao_fat_mutex;
+
+/* Partition information, sector numbers */
+
+static uint8_t partition_type;
+static sector_t partition_start, partition_end;
+
+#define AO_FAT_BAD_CLUSTER 0xffffff7
+#define AO_FAT_LAST_CLUSTER 0xfffffff
+#define AO_FAT_IS_LAST_CLUSTER(c) (((c) & 0xffffff8) == 0xffffff8)
+#define AO_FAT_IS_LAST_CLUSTER16(c) (((c) & 0xfff8) == 0xfff8)
+
+
+#define SECTOR_SIZE 512
+#define SECTOR_MASK (SECTOR_SIZE - 1)
+#define SECTOR_SHIFT 9
+
+#define DIRENT_SIZE 32
+
+/* File system parameters */
+static uint8_t sectors_per_cluster;
+static uint32_t bytes_per_cluster;
+static sector_t reserved_sector_count;
+static uint8_t number_fat;
+static dirent_t root_entries;
+static sector_t sectors_per_fat;
+static cluster_t number_cluster;
+static sector_t fat_start;
+static sector_t root_start;
+static sector_t data_start;
+static cluster_t next_free;
+static uint8_t filesystem_full;
+
+/* FAT32 extra data */
+static uint8_t fat32;
+static uint8_t fsinfo_dirty;
+static cluster_t root_cluster;
+static sector_t fsinfo_sector;
+static cluster_t free_count;
+
+/*
+ * Deal with LSB FAT data structures
+ */
+static uint32_t
+get_u32(uint8_t *base)
+{
+ return ((uint32_t) base[0] |
+ ((uint32_t) base[1] << 8) |
+ ((uint32_t) base[2] << 16) |
+ ((uint32_t) base[3] << 24));
+}
+
+static void
+put_u32(uint8_t *base, uint32_t value)
+{
+ base[0] = value;
+ base[1] = value >> 8;
+ base[2] = value >> 16;
+ base[3] = value >> 24;
+}
+
+static uint16_t
+get_u16(uint8_t *base)
+{
+ return ((uint16_t) base[0] | ((uint16_t) base[1] << 8));
+}
+
+static void
+put_u16(uint8_t *base, uint16_t value)
+{
+ base[0] = value;
+ base[1] = value >> 8;
+}
+
+static uint8_t
+_ao_fat_cluster_valid(cluster_t cluster)
+{
+ return (2 <= cluster && cluster < number_cluster);
+}
+
+/* Start using a sector */
+static uint8_t *
+_ao_fat_sector_get(sector_t sector)
+{
+ sector += partition_start;
+ if (sector >= partition_end)
+ return NULL;
+ return ao_bufio_get(sector);
+}
+
+/* Finish using a sector, 'w' is 1 if modified */
+#define _ao_fat_sector_put(b,w) ao_bufio_put(b,w)
+
+/* Get the next cluster entry in the chain */
+static cluster_t
+_ao_fat_entry_read(cluster_t cluster)
+{
+ sector_t sector;
+ cluster_t offset;
+ uint8_t *buf;
+ cluster_t ret;
+
+ if (!_ao_fat_cluster_valid(cluster))
+ return 0xfffffff7;
+
+ if (fat32)
+ cluster <<= 2;
+ else
+ cluster <<= 1;
+ sector = cluster >> (SECTOR_SHIFT);
+ offset = cluster & SECTOR_MASK;
+ buf = _ao_fat_sector_get(fat_start + sector);
+ if (!buf)
+ return 0;
+
+ if (fat32) {
+ ret = get_u32(buf + offset);
+ ret &= 0xfffffff;
+ } else {
+ ret = get_u16(buf + offset);
+ if (AO_FAT_IS_LAST_CLUSTER16(ret))
+ ret |= 0xfff0000;
+ }
+ _ao_fat_sector_put(buf, 0);
+ return ret;
+}
+
+/* Replace the referenced cluster entry in the chain with
+ * 'new_value'. Return the previous value.
+ */
+static cluster_t
+_ao_fat_entry_replace(cluster_t cluster, cluster_t new_value)
+{
+ sector_t sector;
+ cluster_offset_t offset;
+ uint8_t *buf;
+ cluster_t ret;
+ cluster_t old_value;
+ uint8_t fat;
+
+ if (!_ao_fat_cluster_valid(cluster))
+ return 0xfffffff7;
+
+ /* Convert from cluster index to byte index */
+ if (fat32)
+ cluster <<= 2;
+ else
+ cluster <<= 1;
+ sector = cluster >> SECTOR_SHIFT;
+ offset = cluster & SECTOR_MASK;
+
+ new_value &= 0xfffffff;
+ for (fat = 0; fat < number_fat; fat++) {
+ buf = _ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
+ if (!buf)
+ return 0;
+ if (fat32) {
+ old_value = get_u32(buf + offset);
+ put_u32(buf + offset, new_value | (old_value & 0xf0000000));
+ if (fat == 0) {
+ ret = old_value & 0xfffffff;
+
+ /* Track the free count if it wasn't marked
+ * invalid when we mounted the file system
+ */
+ if (free_count != 0xffffffff) {
+ if (new_value && !ret) {
+ --free_count;
+ fsinfo_dirty = 1;
+ } else if (!new_value && ret) {
+ ++free_count;
+ fsinfo_dirty = 1;
+ }
+ }
+ }
+ } else {
+ if (fat == 0) {
+ ret = get_u16(buf + offset);
+ if (AO_FAT_IS_LAST_CLUSTER16(ret))
+ ret |= 0xfff0000;
+ }
+ put_u16(buf + offset, new_value);
+ }
+ _ao_fat_sector_put(buf, 1);
+ }
+ return ret;
+
+}
+
+/*
+ * Walk a cluster chain and mark
+ * all of them as free
+ */
+static void
+_ao_fat_free_cluster_chain(cluster_t cluster)
+{
+ while (_ao_fat_cluster_valid(cluster)) {
+ if (cluster < next_free) {
+ next_free = cluster;
+ fsinfo_dirty = 1;
+ }
+ cluster = _ao_fat_entry_replace(cluster, 0x00000000);
+ }
+}
+
+/*
+ * _ao_fat_cluster_seek
+ *
+ * The basic file system operation -- map a file cluster index to a
+ * partition cluster number. Done by computing the cluster number and
+ * then walking that many clusters from the first cluster. Returns
+ * 0xffff if we walk off the end of the file or the cluster chain
+ * is damaged somehow
+ */
+static cluster_t
+_ao_fat_cluster_seek(cluster_t cluster, cluster_t distance)
+{
+ while (distance) {
+ cluster = _ao_fat_entry_read(cluster);
+ if (!_ao_fat_cluster_valid(cluster))
+ break;
+ distance--;
+ }
+ return cluster;
+}
+
+/*
+ * _ao_fat_cluster_set_size
+ *
+ * Set the number of clusters in the specified chain,
+ * freeing extra ones or alocating new ones as needed
+ *
+ * Returns AO_FAT_BAD_CLUSTER on allocation failure
+ */
+
+static cluster_t
+_ao_fat_cluster_set_size(cluster_t first_cluster, cluster_t size)
+{
+ cluster_t have;
+ cluster_t last_cluster;
+ cluster_t next_cluster;
+
+ /* Walk the cluster chain to the
+ * spot where it needs to change. That
+ * will either be the end of the chain (in case it needs to grow),
+ * or after the desired number of clusters, in which case it needs to shrink
+ */
+ next_cluster = first_cluster;
+ last_cluster = 0;
+ DBG("\tclusters:");
+ for (have = 0; have < size; have++) {
+ DBG(" %08x", next_cluster);
+ if (!_ao_fat_cluster_valid(next_cluster))
+ break;
+ last_cluster = next_cluster;
+ next_cluster = _ao_fat_entry_read(next_cluster);
+ }
+ DBG("\n");
+
+ /* At this point, last_cluster points to the last valid
+ * cluster in the file, if any. That's the spot in the FAT
+ * that needs to be rewritten, either to truncate the file by
+ * writing an END marker, or to extend the file by writing
+ * more clusters. next_cluster will contain the value of the
+ * FAT at last_cluster.
+ *
+ * If this is at the head of the cluster chain, then
+ * last_cluster will be zero and next_cluster will
+ * be the first cluster in the chain.
+ */
+ if (have == size) {
+ /* The file is large enough, truncate as needed */
+ if (_ao_fat_cluster_valid(next_cluster)) {
+ DBG("truncate between %08x and %08x\n", last_cluster, next_cluster);
+ if (last_cluster)
+ /*
+ * Otherwise, rewrite the last cluster
+ * in the chain with a LAST marker
+ */
+ (void) _ao_fat_entry_replace(last_cluster,
+ AO_FAT_LAST_CLUSTER);
+ else
+ /*
+ * If the file is getting erased, then
+ * rewrite the directory entry cluster
+ * value
+ */
+ first_cluster = 0;
+
+ /* Clear the remaining clusters in the chain */
+ _ao_fat_free_cluster_chain(next_cluster);
+
+ /* The file system is no longer full (if it was) */
+ filesystem_full = 0;
+ } else {
+ DBG("unchanged FAT chain\n");
+ /* The chain is already the right length, don't mess with it */
+ ;
+ }
+ } else {
+ cluster_t need;
+ cluster_t free;
+
+ if (filesystem_full)
+ return AO_FAT_BAD_CLUSTER;
+
+ /* Set next_free if it has wrapped or wasn't set before */
+ if (next_free < 2 || number_cluster <= next_free) {
+ next_free = 2;
+ fsinfo_dirty = 1;
+ }
+
+ /* See if there are enough free clusters in the file system */
+ need = size - have;
+
+#define loop_cluster for (free = next_free; need > 0;)
+#define next_cluster \
+ if (++free == number_cluster) \
+ free = 2; \
+ if (free == next_free) \
+ break; \
+
+ loop_cluster {
+ if (!_ao_fat_entry_read(free))
+ need--;
+ next_cluster;
+ }
+
+ /* Still need some, tell the user that we've failed */
+ if (need) {
+ filesystem_full = 1;
+ return AO_FAT_BAD_CLUSTER;
+ }
+
+ /* Now go allocate those clusters and
+ * thread them onto the chain
+ */
+ need = size - have;
+ loop_cluster {
+ if (_ao_fat_entry_read(free) == 0) {
+ next_free = free + 1;
+ if (next_free >= number_cluster)
+ next_free = 2;
+ fsinfo_dirty = 1;
+ DBG("\tadd cluster. old %08x new %08x\n", last_cluster, free);
+ if (last_cluster)
+ _ao_fat_entry_replace(last_cluster, free);
+ else
+ first_cluster = free;
+ last_cluster = free;
+ need--;
+ }
+ next_cluster;
+ }
+#undef loop_cluster
+#undef next_cluster
+ DBG("\tlast cluster %08x\n", last_cluster);
+ /* Mark the new end of the chain */
+ _ao_fat_entry_replace(last_cluster, AO_FAT_LAST_CLUSTER);
+ }
+
+ DBG("\tfirst cluster %08x\n", first_cluster);
+ return first_cluster;
+}
+
+/* Start using a root directory entry */
+static uint8_t *
+_ao_fat_root_get(dirent_t e)
+{
+ offset_t byte = e * DIRENT_SIZE;
+ sector_t sector = byte >> SECTOR_SHIFT;
+ cluster_offset_t offset = byte & SECTOR_MASK;
+ uint8_t *buf;
+
+ if (fat32) {
+ cluster_t cluster_distance = sector / sectors_per_cluster;
+ sector_t sector_index = sector % sectors_per_cluster;
+ cluster_t cluster = _ao_fat_cluster_seek(root_cluster, cluster_distance);
+
+ if (_ao_fat_cluster_valid(cluster))
+ sector = data_start + (cluster-2) * sectors_per_cluster + sector_index;
+ else
+ return NULL;
+ } else {
+ if (e >= root_entries)
+ return NULL;
+ sector = root_start + sector;
+ }
+
+ buf = _ao_fat_sector_get(sector);
+ if (!buf)
+ return NULL;
+ return buf + offset;
+}
+
+/* Finish using a root directory entry, 'w' is 1 if modified */
+static void
+_ao_fat_root_put(uint8_t *root, dirent_t e, uint8_t write)
+{
+ cluster_offset_t offset = ((e * DIRENT_SIZE) & SECTOR_MASK);
+ uint8_t *buf = root - offset;
+
+ _ao_fat_sector_put(buf, write);
+}
+
+/*
+ * _ao_fat_root_extend
+ *
+ * On FAT32, make the root directory at least 'ents' entries long
+ */
+static int8_t
+_ao_fat_root_extend(dirent_t ents)
+{
+ offset_t byte_size;
+ cluster_t cluster_size;
+ if (!fat32)
+ return 0;
+
+ byte_size = (ents + 1) * 0x20;
+ cluster_size = (byte_size + bytes_per_cluster - 1) / bytes_per_cluster;
+ if (_ao_fat_cluster_set_size(root_cluster, cluster_size) != AO_FAT_BAD_CLUSTER)
+ return 1;
+ return 0;
+}
+
+/*
+ * _ao_fat_setup_partition
+ *
+ * Load the boot block and find the first partition
+ */
+static uint8_t
+_ao_fat_setup_partition(void)
+{
+ uint8_t *mbr;
+ uint8_t *partition;
+ uint32_t partition_size;
+
+ mbr = ao_bufio_get(0);
+ if (!mbr)
+ return AO_FAT_FILESYSTEM_MBR_READ_FAILURE;
+
+ /* Check the signature */
+ if (mbr[0x1fe] != 0x55 || mbr[0x1ff] != 0xaa) {
+ DBG ("Invalid MBR signature %02x %02x\n",
+ mbr[0x1fe], mbr[0x1ff]);
+ ao_bufio_put(mbr, 0);
+ return AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE;
+ }
+
+ /* Check to see if it's actually a boot block, in which
+ * case it's presumably not a paritioned device
+ */
+
+ if (mbr[0] == 0xeb) {
+ partition_start = 0;
+ partition_size = get_u16(mbr + 0x13);
+ if (partition_size == 0)
+ partition_size = get_u32(mbr + 0x20);
+ } else {
+ /* Just use the first partition */
+ partition = &mbr[0x1be];
+
+ partition_type = partition[4];
+ switch (partition_type) {
+ case 4: /* FAT16 up to 32M */
+ case 6: /* FAT16 over 32M */
+ break;
+ case 0x0b: /* FAT32 up to 2047GB */
+ case 0x0c: /* FAT32 LBA */
+ break;
+ default:
+ DBG ("Invalid partition type %02x\n", partition_type);
+ ao_bufio_put(mbr, 0);
+ return AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE;
+ }
+
+ partition_start = get_u32(partition+8);
+ partition_size = get_u32(partition+12);
+ if (partition_size == 0) {
+ DBG ("Zero-sized partition\n");
+ ao_bufio_put(mbr, 0);
+ return AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION;
+ }
+ }
+ partition_end = partition_start + partition_size;
+ ao_bufio_put(mbr, 0);
+ return AO_FAT_FILESYSTEM_SUCCESS;
+}
+
+static uint8_t
+_ao_fat_setup_fs(void)
+{
+ uint8_t *boot = _ao_fat_sector_get(0);
+ uint32_t data_sectors;
+
+ if (!boot)
+ return AO_FAT_FILESYSTEM_BOOT_READ_FAILURE;
+
+ /* Check the signature */
+ if (boot[0x1fe] != 0x55 || boot[0x1ff] != 0xaa) {
+ DBG ("Invalid BOOT signature %02x %02x\n",
+ boot[0x1fe], boot[0x1ff]);
+ _ao_fat_sector_put(boot, 0);
+ return AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE;
+ }
+
+ /* Check the sector size */
+ if (get_u16(boot + 0xb) != SECTOR_SIZE) {
+ DBG ("Invalid sector size %d\n",
+ get_u16(boot + 0xb));
+ _ao_fat_sector_put(boot, 0);
+ return AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE;
+ }
+
+ sectors_per_cluster = boot[0xd];
+ bytes_per_cluster = sectors_per_cluster << SECTOR_SHIFT;
+ reserved_sector_count = get_u16(boot+0xe);
+ number_fat = boot[0x10];
+ root_entries = get_u16(boot + 0x11);
+ sectors_per_fat = get_u16(boot+0x16);
+ fat32 = 0;
+ if (sectors_per_fat == 0) {
+ fat32 = 1;
+ sectors_per_fat = get_u32(boot+0x24);
+ root_cluster = get_u32(boot+0x2c);
+ fsinfo_sector = get_u16(boot + 0x30);
+ }
+ _ao_fat_sector_put(boot, 0);
+
+ free_count = 0xffffffff;
+ next_free = 0;
+ if (fat32 && fsinfo_sector) {
+ uint8_t *fsinfo = _ao_fat_sector_get(fsinfo_sector);
+
+ if (fsinfo) {
+ free_count = get_u32(fsinfo + 0x1e8);
+ next_free = get_u32(fsinfo + 0x1ec);
+ _ao_fat_sector_put(fsinfo, 0);
+ }
+ }
+
+ fat_start = reserved_sector_count;;
+ root_start = fat_start + number_fat * sectors_per_fat;
+ data_start = root_start + ((root_entries * DIRENT_SIZE + SECTOR_MASK) >> SECTOR_SHIFT);
+
+ data_sectors = (partition_end - partition_start) - data_start;
+
+ number_cluster = data_sectors / sectors_per_cluster;
+
+ return AO_FAT_FILESYSTEM_SUCCESS;
+}
+
+/*
+ * State for an open file
+ */
+
+struct ao_file {
+ struct ao_fat_dirent *dirent;
+ offset_t offset;
+ offset_t cluster_offset;
+ cluster_t cluster;
+ uint8_t busy;
+};
+
+#define AO_FAT_NFILE 8
+
+static struct ao_fat_dirent ao_file_dirent[AO_FAT_NFILE];
+
+static struct ao_fat_dirent *
+_ao_fat_file_dirent_alloc(struct ao_fat_dirent *want)
+{
+ int8_t d;
+ struct ao_fat_dirent *free = NULL, *dirent;
+
+ for (d = 0; d < AO_FAT_NFILE; d++) {
+
+ dirent = &ao_file_dirent[d];
+ /* See if there's another user of this file already */
+ if (want && dirent->name[0] != 0) {
+ if (dirent->entry == want->entry)
+ return dirent;
+ } else {
+ if (!free) {
+ free = dirent;
+ if (!want)
+ break;
+ }
+ }
+ }
+ if (free && want)
+ *free = *want;
+ return free;
+}
+
+static struct ao_file ao_file_table[AO_FAT_NFILE];
+
+static int8_t
+_ao_fat_fd_alloc(struct ao_fat_dirent *dirent)
+{
+ int8_t fd;
+
+ for (fd = 0; fd < AO_FAT_NFILE; fd++)
+ if (!ao_file_table[fd].busy) {
+ ao_file_table[fd].dirent = _ao_fat_file_dirent_alloc(dirent);
+ ao_file_table[fd].busy = 1;
+ ao_file_table[fd].offset = 0;
+ ao_file_table[fd].cluster_offset = 0;
+ ao_file_table[fd].cluster = ao_file_table[fd].dirent->cluster;
+
+ return fd;
+ }
+ return -AO_FAT_EMFILE;
+}
+
+static void
+_ao_fat_fd_free(int8_t fd)
+{
+ struct ao_file *file = &ao_file_table[fd];
+ struct ao_fat_dirent *dirent = file->dirent;
+ memset(&ao_file_table[fd], '\0', sizeof (struct ao_file));
+
+ /* Check and see if another ao_file references the same dirent */
+ for (fd = 0; fd < AO_FAT_NFILE; fd++)
+ if (ao_file_table[fd].dirent == dirent)
+ return;
+ memset(dirent, '\0', sizeof (struct ao_fat_dirent));
+}
+
+static struct ao_file *
+_ao_fat_fd_to_file(int8_t fd)
+{
+ struct ao_file *file;
+ if (fd < 0 || AO_FAT_NFILE <= fd)
+ return NULL;
+
+ file = &ao_file_table[fd];
+ if (!file->busy)
+ return NULL;
+ return file;
+}
+
+static uint8_t ao_filesystem_setup;
+static uint8_t ao_filesystem_status;
+
+static uint8_t
+_ao_fat_setup(void)
+{
+ if (!ao_filesystem_setup) {
+
+ ao_filesystem_setup = 1;
+ ao_bufio_setup();
+
+ /* Re-initialize all global state; this will help to allow the
+ * file system to get swapped someday
+ */
+ partition_type = partition_start = partition_end = 0;
+ sectors_per_cluster = bytes_per_cluster = reserved_sector_count = 0;
+ number_fat = root_entries = sectors_per_fat = 0;
+ number_cluster = fat_start = root_start = data_start = 0;
+ next_free = filesystem_full = 0;
+ fat32 = fsinfo_dirty = root_cluster = fsinfo_sector = free_count = 0;
+
+ /* Reset open file table */
+ memset(&ao_file_table, '\0', sizeof (ao_file_table));
+
+ ao_filesystem_status = _ao_fat_setup_partition();
+ if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
+ return ao_filesystem_status;
+ ao_filesystem_status = _ao_fat_setup_fs();
+ if (ao_filesystem_status != AO_FAT_FILESYSTEM_SUCCESS)
+ return ao_filesystem_status;
+ }
+ return ao_filesystem_status;
+}
+
+void
+ao_fat_unmount(void)
+{
+ ao_filesystem_setup = 0;
+}
+
+/*
+ * Basic file operations
+ */
+
+static uint32_t
+_ao_fat_current_sector(struct ao_file *file)
+{
+ cluster_t cluster_offset;
+ uint32_t sector_offset;
+ uint16_t sector_index;
+ cluster_t cluster;
+
+ DBG("current sector offset %d size %d\n",
+ file->offset, file->dirent->size);
+
+ if (file->offset > file->dirent->size) {
+ printf ("file offset %d larger than size %d\n",
+ file->offset, file->dirent->size);
+ return 0xffffffff;
+ }
+
+ sector_offset = file->offset >> SECTOR_SHIFT;
+
+ if (!file->cluster || file->offset < file->cluster_offset) {
+ file->cluster = file->dirent->cluster;
+ file->cluster_offset = 0;
+ DBG("\treset to start of file %08x\n", file->cluster);
+ }
+
+ if (file->cluster_offset + bytes_per_cluster <= file->offset) {
+ cluster_t cluster_distance;
+
+ cluster_offset = sector_offset / sectors_per_cluster;
+
+ cluster_distance = cluster_offset - file->cluster_offset / bytes_per_cluster;
+
+ DBG("\tseek forward %d clusters\n", cluster_distance);
+ cluster = _ao_fat_cluster_seek(file->cluster, cluster_distance);
+
+ if (!_ao_fat_cluster_valid(cluster)) {
+ printf ("invalid cluster %08x\n", cluster);
+ return 0xffffffff;
+ }
+ file->cluster = cluster;
+ file->cluster_offset = cluster_offset * bytes_per_cluster;
+ }
+
+ sector_index = sector_offset % sectors_per_cluster;
+ DBG("current cluster %08x sector_index %d sector %d\n",
+ file->cluster, sector_index,
+ data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index);
+ return data_start + (uint32_t) (file->cluster-2) * sectors_per_cluster + sector_index;
+}
+
+/*
+ * _ao_fat_invaldate_cluster_offset
+ *
+ * When the file size gets shrunk, invalidate
+ * any file structures referencing clusters beyond that point
+ */
+
+static void
+_ao_fat_invalidate_cluster_offset(struct ao_fat_dirent *dirent)
+{
+ int8_t fd;
+ struct ao_file *file;
+
+ for (fd = 0; fd < AO_FAT_NFILE; fd++) {
+ file = &ao_file_table[fd];
+ if (!file->busy)
+ continue;
+ if (file->dirent == dirent) {
+ if (file->cluster_offset >= dirent->size) {
+ file->cluster_offset = 0;
+ file->cluster = dirent->cluster;
+ }
+ }
+ }
+}
+
+
+/*
+ * _ao_fat_set_size
+ *
+ * Set the size of the current file, truncating or extending
+ * the cluster chain as needed
+ */
+static int8_t
+_ao_fat_set_size(struct ao_file *file, uint32_t size)
+{
+ uint8_t *dent;
+ cluster_t first_cluster;
+ cluster_t have_clusters, need_clusters;
+
+ DBG ("Set size %d\n", size);
+ if (size == file->dirent->size) {
+ DBG("\tsize match\n");
+ return AO_FAT_SUCCESS;
+ }
+
+ first_cluster = file->dirent->cluster;
+ have_clusters = (file->dirent->size + bytes_per_cluster - 1) / bytes_per_cluster;
+ need_clusters = (size + bytes_per_cluster - 1) / bytes_per_cluster;
+
+ DBG ("\tfirst cluster %08x have %d need %d\n", first_cluster, have_clusters, need_clusters);
+ if (have_clusters != need_clusters) {
+ if (file->cluster && size > file->cluster_offset) {
+ cluster_t offset_clusters = (file->cluster_offset + bytes_per_cluster) / bytes_per_cluster;
+ cluster_t extra_clusters = need_clusters - offset_clusters;
+ cluster_t next_cluster;
+
+ DBG ("\tset size relative offset_clusters %d extra_clusters %d\n",
+ offset_clusters, extra_clusters);
+
+ /* Need one more to account for file->cluster, which we already have */
+ next_cluster = _ao_fat_cluster_set_size(file->cluster, extra_clusters + 1);
+ if (next_cluster == AO_FAT_BAD_CLUSTER)
+ return -AO_FAT_ENOSPC;
+ } else {
+ DBG ("\tset size absolute need_clusters %d\n", need_clusters);
+ first_cluster = _ao_fat_cluster_set_size(first_cluster, need_clusters);
+
+ if (first_cluster == AO_FAT_BAD_CLUSTER)
+ return -AO_FAT_ENOSPC;
+ }
+ }
+
+ DBG ("\tupdate directory size\n");
+ /* Update the directory entry */
+ dent = _ao_fat_root_get(file->dirent->entry);
+ if (!dent) {
+ printf ("dent update failed\n");
+ return -AO_FAT_EIO;
+ }
+ put_u32(dent + 0x1c, size);
+ put_u16(dent + 0x1a, first_cluster);
+ if (fat32)
+ put_u16(dent + 0x14, first_cluster >> 16);
+ _ao_fat_root_put(dent, file->dirent->entry, 1);
+
+ file->dirent->size = size;
+ file->dirent->cluster = first_cluster;
+ if (have_clusters > need_clusters)
+ _ao_fat_invalidate_cluster_offset(file->dirent);
+ DBG ("set size done\n");
+ return AO_FAT_SUCCESS;
+}
+
+/*
+ * _ao_fat_root_init
+ *
+ * Initialize a root directory entry
+ */
+static void
+_ao_fat_root_init(uint8_t *dent, char name[11], uint8_t attr)
+{
+ memset(dent, '\0', 0x20);
+ memmove(dent, name, 11);
+
+ dent[0x0b] = 0x00;
+ dent[0x0c] = 0x00;
+ dent[0x0d] = 0x00;
+
+ /* XXX fix time */
+ put_u16(dent + 0x0e, 0);
+ /* XXX fix date */
+ put_u16(dent + 0x10, 0);
+ /* XXX fix date */
+ put_u16(dent + 0x12, 0);
+
+ /* XXX fix time */
+ put_u16(dent + 0x16, 0);
+ /* XXX fix date */
+ put_u16(dent + 0x18, 0);
+
+ /* cluster number */
+ /* Low cluster bytes */
+ put_u16(dent + 0x1a, 0);
+ /* FAT32 high cluster bytes */
+ put_u16(dent + 0x14, 0);
+
+ /* size */
+ put_u32(dent + 0x1c, 0);
+}
+
+
+static void
+_ao_fat_dirent_init(struct ao_fat_dirent *dirent, uint8_t *dent, uint16_t entry)
+{
+ memcpy(dirent->name, dent + 0x00, 11);
+ dirent->attr = dent[0x0b];
+ dirent->size = get_u32(dent+0x1c);
+ dirent->cluster = get_u16(dent+0x1a);
+ if (fat32)
+ dirent->cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
+ dirent->entry = entry;
+}
+
+/*
+ * _ao_fat_flush_fsinfo
+ *
+ * Write out any fsinfo changes to disk
+ */
+
+static void
+_ao_fat_flush_fsinfo(void)
+{
+ uint8_t *fsinfo;
+
+ if (!fat32)
+ return;
+
+ if (!fsinfo_dirty)
+ return;
+ fsinfo_dirty = 0;
+ if (!fsinfo_sector)
+ return;
+
+ fsinfo = _ao_fat_sector_get(fsinfo_sector);
+ if (fsinfo) {
+ put_u32(fsinfo + 0x1e8, free_count);
+ put_u32(fsinfo + 0x1ec, next_free);
+ _ao_fat_sector_put(fsinfo, 1);
+ }
+}
+
+/*
+ * Public API
+ */
+
+/*
+ * ao_fat_sync
+ *
+ * Flush any pending I/O to storage
+ */
+
+static void
+_ao_fat_sync(void)
+{
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+ return;
+ _ao_fat_flush_fsinfo();
+ ao_bufio_flush();
+}
+
+void
+ao_fat_sync(void)
+{
+ ao_mutex_get(&ao_fat_mutex);
+ _ao_fat_sync();
+ ao_mutex_put(&ao_fat_mutex);
+}
+
+/*
+ * ao_fat_full
+ *
+ * Returns TRUE if the filesystem cannot take
+ * more data
+ */
+
+int8_t
+ao_fat_full(void)
+{
+ ao_mutex_get(&ao_fat_mutex);
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
+ ao_mutex_put(&ao_fat_mutex);
+ return 1;
+ }
+ ao_mutex_put(&ao_fat_mutex);
+ return filesystem_full;
+}
+
+static int8_t
+_ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
+{
+ uint8_t *dent;
+
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+ return -AO_FAT_EIO;
+
+ for (;;) {
+ dent = _ao_fat_root_get(*entry);
+ if (!dent)
+ return -AO_FAT_EDIREOF;
+
+ if (dent[0] == AO_FAT_DENT_END) {
+ _ao_fat_root_put(dent, *entry, 0);
+ return -AO_FAT_EDIREOF;
+ }
+ if (dent[0] != AO_FAT_DENT_EMPTY && (dent[0xb] & 0xf) != 0xf) {
+ _ao_fat_dirent_init(dirent, dent, *entry);
+ _ao_fat_root_put(dent, *entry, 0);
+ (*entry)++;
+ return AO_FAT_SUCCESS;
+ }
+ _ao_fat_root_put(dent, *entry, 0);
+ (*entry)++;
+ }
+}
+
+int8_t
+ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent)
+{
+ int8_t status;
+
+ ao_mutex_get(&ao_fat_mutex);
+ status = _ao_fat_readdir(entry, dirent);
+ ao_mutex_put(&ao_fat_mutex);
+ return status;
+}
+
+/*
+ * ao_fat_open
+ *
+ * Open an existing file.
+ */
+static int8_t
+_ao_fat_open(char name[11], uint8_t mode)
+{
+ uint16_t entry = 0;
+ struct ao_fat_dirent dirent;
+ int8_t status;
+
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+ return -AO_FAT_EIO;
+
+ for (;;) {
+ status = _ao_fat_readdir(&entry, &dirent);
+ if (status < 0) {
+ if (status == -AO_FAT_EDIREOF)
+ return -AO_FAT_ENOENT;
+ return status;
+ }
+ if (!memcmp(name, dirent.name, 11)) {
+ if (AO_FAT_IS_DIR(dirent.attr))
+ return -AO_FAT_EISDIR;
+ if (!AO_FAT_IS_FILE(dirent.attr))
+ return -AO_FAT_EPERM;
+ if (mode > AO_FAT_OPEN_READ && (dirent.attr & AO_FAT_FILE_READ_ONLY))
+ return -AO_FAT_EACCESS;
+ return _ao_fat_fd_alloc(&dirent);
+ }
+ }
+ return -AO_FAT_ENOENT;
+}
+
+int8_t
+ao_fat_open(char name[11], uint8_t mode)
+{
+ int8_t status;
+
+ ao_mutex_get(&ao_fat_mutex);
+ status = _ao_fat_open(name, mode);
+ ao_mutex_put(&ao_fat_mutex);
+ return status;
+}
+
+/*
+ * ao_fat_close
+ *
+ * Close the currently open file
+ */
+static int8_t
+_ao_fat_close(int8_t fd)
+{
+ struct ao_file *file;
+
+ file = _ao_fat_fd_to_file(fd);
+ if (!file)
+ return -AO_FAT_EBADF;
+
+ _ao_fat_fd_free(fd);
+ _ao_fat_sync();
+ return AO_FAT_SUCCESS;
+}
+
+int8_t
+ao_fat_close(int8_t fd)
+{
+ int8_t status;
+
+ ao_mutex_get(&ao_fat_mutex);
+ status = _ao_fat_close(fd);
+ ao_mutex_put(&ao_fat_mutex);
+ return status;
+}
+
+/*
+ * ao_fat_creat
+ *
+ * Open and truncate an existing file or
+ * create a new file
+ */
+
+static int8_t
+_ao_fat_creat(char name[11])
+{
+ uint16_t entry;
+ int8_t fd;
+ int8_t status;
+ uint8_t *dent;
+ struct ao_file *file;
+
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS)
+ return -AO_FAT_EIO;
+
+ fd = _ao_fat_open(name, AO_FAT_OPEN_WRITE);
+ if (fd >= 0) {
+ file = &ao_file_table[fd];
+ status = _ao_fat_set_size(file, 0);
+ if (status < 0) {
+ _ao_fat_close(fd);
+ fd = status;
+ }
+ } else {
+ if (fd == -AO_FAT_ENOENT) {
+ entry = 0;
+ for (;;) {
+ dent = _ao_fat_root_get(entry);
+ if (!dent) {
+
+ if (_ao_fat_root_extend(entry))
+ continue;
+ fd = -AO_FAT_ENOSPC;
+ break;
+ }
+ if (dent[0] == AO_FAT_DENT_EMPTY || dent[0] == AO_FAT_DENT_END) {
+ fd = _ao_fat_fd_alloc(NULL);
+ if (fd < 0) {
+ _ao_fat_root_put(dent, entry, 0);
+ break;
+ }
+
+ file = &ao_file_table[fd];
+ /* Initialize the dent */
+ _ao_fat_root_init(dent, name, AO_FAT_FILE_REGULAR);
+
+ /* Now initialize the dirent from the dent */
+ _ao_fat_dirent_init(file->dirent, dent, entry);
+
+ /* And write the dent to storage */
+ _ao_fat_root_put(dent, entry, 1);
+
+ status = -AO_FAT_SUCCESS;
+ break;
+ } else {
+ _ao_fat_root_put(dent, entry, 0);
+ }
+ entry++;
+ }
+ }
+ }
+ return fd;
+}
+
+int8_t
+ao_fat_creat(char name[11])
+{
+ int8_t status;
+
+ ao_mutex_get(&ao_fat_mutex);
+ status = _ao_fat_creat(name);
+ ao_mutex_put(&ao_fat_mutex);
+ return status;
+}
+
+/*
+ * ao_fat_map_current
+ *
+ * Map the sector pointed at by the current file offset
+ */
+
+static void *
+ao_fat_map_current(struct ao_file *file, int len, cluster_offset_t *offsetp, cluster_offset_t *this_time)
+{
+ cluster_offset_t offset;
+ sector_t sector;
+ void *buf;
+
+ offset = file->offset & SECTOR_MASK;
+ sector = _ao_fat_current_sector(file);
+ if (sector == 0xffffffff) {
+ printf ("invalid sector at offset %d\n", file->offset);
+ return NULL;
+ }
+ buf = _ao_fat_sector_get(sector);
+ if (!buf)
+ printf ("sector get failed. Sector %d. Partition end %d\n", sector, partition_end);
+ if (offset + len < SECTOR_SIZE)
+ *this_time = len;
+ else
+ *this_time = SECTOR_SIZE - offset;
+ *offsetp = offset;
+ return buf;
+}
+
+/*
+ * ao_fat_read
+ *
+ * Read from the file
+ */
+int
+ao_fat_read(int8_t fd, void *dst, int len)
+{
+ uint8_t *dst_b = dst;
+ cluster_offset_t this_time;
+ cluster_offset_t offset;
+ uint8_t *buf;
+ int ret = 0;
+ struct ao_file *file;
+
+ ao_mutex_get(&ao_fat_mutex);
+ file = _ao_fat_fd_to_file(fd);
+ if (!file) {
+ ret = -AO_FAT_EBADF;
+ goto done;
+ }
+
+ if (file->offset + len > file->dirent->size)
+ len = file->dirent->size - file->offset;
+
+ if (len < 0)
+ len = 0;
+
+ while (len) {
+ buf = ao_fat_map_current(file, len, &offset, &this_time);
+ if (!buf) {
+ printf ("map_current failed\n");
+ ret = -AO_FAT_EIO;
+ break;
+ }
+ memcpy(dst_b, buf + offset, this_time);
+ _ao_fat_sector_put(buf, 0);
+
+ ret += this_time;
+ len -= this_time;
+ dst_b += this_time;
+ file->offset = file->offset + this_time;
+ }
+done:
+ ao_mutex_put(&ao_fat_mutex);
+ return ret;
+}
+
+/*
+ * ao_fat_write
+ *
+ * Write to the file, extended as necessary
+ */
+int
+ao_fat_write(int8_t fd, void *src, int len)
+{
+ uint8_t *src_b = src;
+ cluster_offset_t this_time;
+ cluster_offset_t offset;
+ uint8_t *buf;
+ int ret = 0;
+ struct ao_file *file;
+
+ ao_mutex_get(&ao_fat_mutex);
+ file = _ao_fat_fd_to_file(fd);
+ if (!file) {
+ ret = -AO_FAT_EBADF;
+ goto done;
+ }
+
+ if (file->offset + len > file->dirent->size) {
+ ret = _ao_fat_set_size(file, file->offset + len);
+ if (ret < 0)
+ goto done;
+ }
+
+ while (len) {
+ buf = ao_fat_map_current(file, len, &offset, &this_time);
+ if (!buf) {
+ printf ("map_current failed\n");
+ ret = -AO_FAT_EIO;
+ break;
+ }
+ memcpy(buf + offset, src_b, this_time);
+ _ao_fat_sector_put(buf, 1);
+
+ ret += this_time;
+ len -= this_time;
+ src_b += this_time;
+ file->offset = file->offset + this_time;
+ }
+done:
+ ao_mutex_put(&ao_fat_mutex);
+ return ret;
+}
+
+/*
+ * ao_fat_seek
+ *
+ * Set the position for the next I/O operation
+ * Note that this doesn't actually change the size
+ * of the file if the requested position is beyond
+ * the current file length, that would take a future
+ * write
+ */
+int32_t
+ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence)
+{
+ offset_t new_offset;
+ struct ao_file *file;
+ int32_t ret;
+
+ ao_mutex_get(&ao_fat_mutex);
+ file = _ao_fat_fd_to_file(fd);
+ if (!file) {
+ ret = -AO_FAT_EBADF;
+ goto done;
+ }
+
+ new_offset = file->offset;
+ switch (whence) {
+ case AO_FAT_SEEK_SET:
+ new_offset = pos;
+ break;
+ case AO_FAT_SEEK_CUR:
+ new_offset += pos;
+ break;
+ case AO_FAT_SEEK_END:
+ new_offset = file->dirent->size + pos;
+ break;
+ }
+ ret = file->offset = new_offset;
+done:
+ ao_mutex_put(&ao_fat_mutex);
+ return ret;
+}
+
+/*
+ * ao_fat_unlink
+ *
+ * Remove a file from the directory, marking
+ * all clusters as free
+ */
+int8_t
+ao_fat_unlink(char name[11])
+{
+ uint16_t entry = 0;
+ struct ao_fat_dirent dirent;
+ int8_t ret;
+
+ ao_mutex_get(&ao_fat_mutex);
+ if (_ao_fat_setup() != AO_FAT_FILESYSTEM_SUCCESS) {
+ ret = -AO_FAT_EIO;
+ goto done;
+ }
+
+ while (ao_fat_readdir(&entry, &dirent)) {
+ if (memcmp(name, dirent.name, 11) == 0) {
+ uint8_t *next;
+ uint8_t *ent;
+ uint8_t delete;
+
+ if (AO_FAT_IS_DIR(dirent.attr)) {
+ ret = -AO_FAT_EISDIR;
+ goto done;
+ }
+ if (!AO_FAT_IS_FILE(dirent.attr)) {
+ ret = -AO_FAT_EPERM;
+ goto done;
+ }
+
+ _ao_fat_free_cluster_chain(dirent.cluster);
+ next = _ao_fat_root_get(dirent.entry + 1);
+ if (next && next[0] != AO_FAT_DENT_END)
+ delete = AO_FAT_DENT_EMPTY;
+ else
+ delete = AO_FAT_DENT_END;
+ if (next)
+ _ao_fat_root_put(next, dirent.entry + 1, 0);
+ ent = _ao_fat_root_get(dirent.entry);
+ if (ent) {
+ memset(ent, '\0', DIRENT_SIZE);
+ *ent = delete;
+ _ao_fat_root_put(ent, dirent.entry, 1);
+ }
+ ao_bufio_flush();
+ ret = AO_FAT_SUCCESS;
+ goto done;
+ }
+ }
+ ret = -AO_FAT_ENOENT;
+done:
+ ao_mutex_put(&ao_fat_mutex);
+ return ret;
+}
+
+int8_t
+ao_fat_rename(char old[11], char new[11])
+{
+ return -AO_FAT_EIO;
+}
+
+#if FAT_COMMANDS
+
+static const char *filesystem_errors[] = {
+ [AO_FAT_FILESYSTEM_SUCCESS] = "FAT file system operating normally",
+ [AO_FAT_FILESYSTEM_MBR_READ_FAILURE] = "MBR media read error",
+ [AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE] = "MBR signature invalid",
+ [AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE] = "Unsupported paritition type",
+ [AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION] = "Partition has zero sectors",
+ [AO_FAT_FILESYSTEM_BOOT_READ_FAILURE] = "Boot block media read error",
+ [AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE] = "Boot block signature invalid",
+ [AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE] = "Sector size not 512",
+};
+
+static void
+ao_fat_mbr_cmd(void)
+{
+ uint8_t status;
+
+ ao_mutex_get(&ao_fat_mutex);
+ status = _ao_fat_setup();
+ if (status == AO_FAT_FILESYSTEM_SUCCESS) {
+ printf ("partition type: %02x\n", partition_type);
+ printf ("partition start: %08x\n", partition_start);
+
+ printf ("partition end: %08x\n", partition_end);
+
+ printf ("fat32: %d\n", fat32);
+ printf ("sectors per cluster %d\n", sectors_per_cluster);
+ printf ("reserved sectors %d\n", reserved_sector_count);
+ printf ("number of FATs %d\n", number_fat);
+ printf ("root entries %d\n", root_entries);
+ printf ("sectors per fat %d\n", sectors_per_fat);
+
+ printf ("fat start %d\n", fat_start);
+ printf ("root start %d\n", root_start);
+ printf ("data start %d\n", data_start);
+ } else {
+ printf ("FAT filesystem not available: %s\n", filesystem_errors[status]);
+ }
+ ao_mutex_put(&ao_fat_mutex);
+}
+
+struct ao_fat_attr {
+ uint8_t bit;
+ char label;
+};
+
+static const struct ao_fat_attr ao_fat_attr[] = {
+ { .bit = AO_FAT_FILE_READ_ONLY, .label = 'R' },
+ { .bit = AO_FAT_FILE_HIDDEN, .label = 'H' },
+ { .bit = AO_FAT_FILE_SYSTEM, .label = 'S' },
+ { .bit = AO_FAT_FILE_VOLUME_LABEL, .label = 'V' },
+ { .bit = AO_FAT_FILE_DIRECTORY, .label = 'D' },
+ { .bit = AO_FAT_FILE_ARCHIVE, .label = 'A' },
+};
+
+#define NUM_FAT_ATTR (sizeof (ao_fat_attr) / sizeof (ao_fat_attr[0]))
+
+static void
+ao_fat_list_cmd(void)
+{
+ uint16_t entry = 0;
+ struct ao_fat_dirent dirent;
+ int i;
+ int8_t status;
+
+ while ((status = ao_fat_readdir(&entry, &dirent)) == AO_FAT_SUCCESS) {
+ for (i = 0; i < 8; i++)
+ putchar(dirent.name[i]);
+ putchar('.');
+ for (; i < 11; i++)
+ putchar(dirent.name[i]);
+ for (i = 0; i < NUM_FAT_ATTR; i++)
+ putchar (dirent.attr & ao_fat_attr[i].bit ? ao_fat_attr[i].label : ' ');
+ printf (" @%08x %d\n", dirent.cluster, dirent.size);
+ }
+ if (status != -AO_FAT_EDIREOF)
+ printf ("readdir failed: %d\n", status);
+}
+
+static uint8_t
+ao_fat_parse_name(char name[11])
+{
+ uint8_t c;
+
+ name[0] = '\0';
+ ao_cmd_white();
+ c = 0;
+ while (ao_cmd_lex_c != '\n') {
+ if (ao_cmd_lex_c == '.') {
+ for (; c < 8; c++)
+ name[c] = ' ';
+ } else {
+ if (c < 11)
+ name[c++] = ao_cmd_lex_c;
+ }
+ ao_cmd_lex();
+ }
+ while (c < 11)
+ name[c++] = ' ';
+}
+
+static void
+ao_fat_dump_cmd(void)
+{
+ char name[11];
+ int8_t fd;
+ int cnt, i;
+ char buf[32];
+
+ ao_fat_parse_name(name);
+ if (name[0] == '\0') {
+ ao_cmd_status = ao_cmd_syntax_error;
+ return;
+ }
+
+ fd = ao_fat_open(name, AO_FAT_OPEN_READ);
+ if (fd < 0) {
+ printf ("Open failed: %d\n", fd);
+ return;
+ }
+ while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
+ for (i = 0; i < cnt; i++)
+ putchar(buf[i]);
+ }
+ ao_fat_close(fd);
+}
+
+static void
+ao_fat_write_cmd(void)
+{
+ char name[11];
+ int8_t fd;
+ int cnt, i;
+ char buf[64];
+ char c;
+ int status;
+
+ ao_fat_parse_name(name);
+ if (name[0] == '\0') {
+ ao_cmd_status = ao_cmd_syntax_error;
+ return;
+ }
+
+ fd = ao_fat_creat(name);
+ if (fd < 0) {
+ printf ("Open failed: %d\n", fd);
+ return;
+ }
+ flush();
+ while ((c = getchar()) != 4) {
+ if (c == '\r') c = '\n';
+ if (ao_echo()) {
+ if (c == '\n') putchar ('\r');
+ putchar(c); flush();
+ }
+ status = ao_fat_write(fd, &c, 1);
+ if (status != 1) {
+ printf ("Write failure %d\n", status);
+ break;
+ }
+ }
+ ao_fat_close(fd);
+}
+
+static void
+put32(uint32_t a)
+{
+ ao_cmd_put16(a >> 16);
+ ao_cmd_put16(a);
+}
+
+static void
+ao_fat_hexdump_cmd(void)
+{
+ char name[11];
+ int8_t fd;
+ int cnt, i;
+ char buf[8];
+ uint32_t addr;
+
+ ao_fat_parse_name(name);
+ if (name[0] == '\0') {
+ ao_cmd_status = ao_cmd_syntax_error;
+ return;
+ }
+
+ fd = ao_fat_open(name, AO_FAT_OPEN_READ);
+ if (fd < 0) {
+ printf ("Open failed: %d\n", fd);
+ return;
+ }
+ addr = 0;
+ while ((cnt = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
+ put32(addr);
+ for (i = 0; i < cnt; i++) {
+ putchar(' ');
+ ao_cmd_put8(buf[i]);
+ }
+ putchar('\n');
+ addr += cnt;
+ }
+ ao_fat_close(fd);
+}
+
+static const struct ao_cmds ao_fat_cmds[] = {
+ { ao_fat_mbr_cmd, "M\0Show FAT MBR and other info" },
+ { ao_fat_list_cmd, "F\0List FAT directory" },
+ { ao_fat_dump_cmd, "D <name>\0Dump FAT file" },
+ { ao_fat_write_cmd, "W <name>\0Write FAT file (end with ^D)" },
+ { ao_fat_hexdump_cmd, "H <name>\0HEX dump FAT file" },
+ { 0, NULL },
+};
+
+#endif
+
+void
+ao_fat_init(void)
+{
+ ao_bufio_init();
+ ao_cmd_register(&ao_fat_cmds[0]);
+}
--- /dev/null
+/*
+ * Copyright © 2013 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.
+ */
+
+#ifndef _AO_FAT_H_
+#define _AO_FAT_H_
+
+void
+ao_fat_init(void);
+
+#define AO_FAT_FILE_REGULAR 0x00
+#define AO_FAT_FILE_READ_ONLY 0x01
+#define AO_FAT_FILE_HIDDEN 0x02
+#define AO_FAT_FILE_SYSTEM 0x04
+#define AO_FAT_FILE_VOLUME_LABEL 0x08
+#define AO_FAT_FILE_DIRECTORY 0x10
+#define AO_FAT_FILE_ARCHIVE 0x20
+
+#define AO_FAT_DENT_EMPTY 0xe5
+#define AO_FAT_DENT_END 0x00
+
+#define AO_FAT_IS_FILE(attr) (((attr) & (AO_FAT_FILE_VOLUME_LABEL|AO_FAT_FILE_DIRECTORY)) == 0)
+#define AO_FAT_IS_DIR(attr) (((attr) & (AO_FAT_FILE_DIRECTORY|AO_FAT_FILE_VOLUME_LABEL)) == AO_FAT_FILE_DIRECTORY)
+
+/* API error codes */
+#define AO_FAT_SUCCESS 0
+#define AO_FAT_EPERM 1
+#define AO_FAT_ENOENT 2
+#define AO_FAT_EIO 4
+#define AO_FAT_EBADF 9
+#define AO_FAT_EACCESS 13
+#define AO_FAT_EEXIST 17
+#define AO_FAT_ENOTDIR 20
+#define AO_FAT_EISDIR 21
+#define AO_FAT_EMFILE 24
+#define AO_FAT_EFBIG 27
+#define AO_FAT_ENOSPC 28
+#define AO_FAT_EDIREOF 29
+
+/* ao_fat_setup return values */
+#define AO_FAT_FILESYSTEM_SUCCESS 0
+#define AO_FAT_FILESYSTEM_MBR_READ_FAILURE 1
+#define AO_FAT_FILESYSTEM_INVALID_MBR_SIGNATURE 2
+#define AO_FAT_FILESYSTEM_INVALID_PARTITION_TYPE 3
+#define AO_FAT_FILESYSTEM_ZERO_SIZED_PARTITION 4
+
+#define AO_FAT_FILESYSTEM_BOOT_READ_FAILURE 5
+#define AO_FAT_FILESYSTEM_INVALID_BOOT_SIGNATURE 6
+#define AO_FAT_FILESYSTEM_INVALID_SECTOR_SIZE 7
+
+void
+ao_fat_sync(void);
+
+void
+ao_fat_unmount(void);
+
+int8_t
+ao_fat_full(void);
+
+int8_t
+ao_fat_open(char name[11], uint8_t mode);
+
+#define AO_FAT_OPEN_READ 0
+#define AO_FAT_OPEN_WRITE 1
+#define AO_FAT_OPEN_RW 2
+
+int8_t
+ao_fat_creat(char name[11]);
+
+int8_t
+ao_fat_close(int8_t fd);
+
+int
+ao_fat_read(int8_t fd, void *dest, int len);
+
+int
+ao_fat_write(int8_t fd, void *src, int len);
+
+#define AO_FAT_SEEK_SET 0
+#define AO_FAT_SEEK_CUR 1
+#define AO_FAT_SEEK_END 2
+
+int32_t
+ao_fat_seek(int8_t fd, int32_t pos, uint8_t whence);
+
+int8_t
+ao_fat_unlink(char name[11]);
+
+int8_t
+ao_fat_rename(char old[11], char new[11]);
+
+/*
+ * Byte offset within a file. Supports files up to 2GB in size
+ */
+typedef int32_t ao_fat_offset_t;
+
+/*
+ * Cluster index in partition data space
+ */
+typedef uint32_t ao_fat_cluster_t;
+
+/*
+ * Sector offset within partition
+ */
+typedef uint32_t ao_fat_sector_t;
+
+/*
+ * Index within the root directory
+ */
+typedef uint16_t ao_fat_dirent_t;
+
+/*
+ * Offset within a cluster (or sector)
+ */
+typedef uint16_t ao_fat_cluster_offset_t;
+
+struct ao_fat_dirent {
+ char name[11];
+ uint8_t attr;
+ uint32_t size;
+ ao_fat_cluster_t cluster;
+ uint16_t entry;
+};
+
+int8_t
+ao_fat_readdir(uint16_t *entry, struct ao_fat_dirent *dirent);
+
+#endif /* _AO_FAT_H_ */
--- /dev/null
+/*
+ * Copyright © 2013 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 "ao.h"
+#include "ao_log.h"
+#include "ao_fat.h"
+
+static uint8_t log_year, log_month, log_day;
+static uint8_t log_open;
+static int8_t log_fd;
+static uint8_t log_mutex;
+
+static void
+ao_log_open(void)
+{
+ char name[12];
+ int8_t status;
+
+ sprintf(name,"%04d%02d%02dLOG", 2000 + log_year, log_month, log_day);
+ status = ao_fat_open(name, AO_FAT_OPEN_WRITE);
+ if (status >= 0) {
+ log_fd = status;
+ ao_fat_seek(log_fd, 0, AO_FAT_SEEK_END);
+ log_open = 1;
+ } else if (status == -AO_FAT_ENOENT) {
+ status = ao_fat_creat(name);
+ if (status == AO_FAT_SUCCESS) {
+ log_fd = status;
+ log_open = 1;
+ }
+ }
+}
+
+static void
+ao_log_close(void)
+{
+ if (log_open) {
+ log_open = 0;
+ ao_fat_close(log_fd);
+ log_fd = -1;
+ }
+}
+
+uint8_t
+ao_log_full(void)
+{
+ return ao_fat_full();
+}
+
+uint8_t
+ao_log_mega(struct ao_log_mega *log)
+{
+ uint8_t wrote = 0;
+ ao_mutex_get(&log_mutex);
+ if (log->type == AO_LOG_GPS_TIME) {
+ if (log_open &&
+ (log_year != log->u.gps.year ||
+ log_month != log->u.gps.month ||
+ log_day != log->u.gps.day)) {
+ ao_log_close();
+ }
+ if (!log_open) {
+ log_year = log->u.gps.year;
+ log_month = log->u.gps.month;
+ log_day = log->u.gps.day;
+ ao_log_open();
+ }
+ }
+ if (log_open) {
+ wrote = ao_fat_write(log_fd, log, sizeof (*log)) == AO_FAT_SUCCESS;
+ ao_fat_sync();
+ }
+ ao_mutex_put(&log_mutex);
+ return wrote;
+}
+
+void
+ao_log_flush(void)
+{
+ ao_fat_sync();
+}
--- /dev/null
+/*
+ * Copyright © 2013 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 "ao.h"
+
+static void
+ao_rfpa0133_set_power(uint8_t power)
+{
+ ao_gpio_set(AO_PA_GAIN_8_GPIO, AO_PA_GAIN_8_PIN, AO_PA_GAIN_8, power & 1);
+ ao_gpio_set(AO_PA_GAIN_16_GPIO, AO_PA_GAIN_16_PIN, AO_PA_GAIN_16, (power >> 1) & 1);
+}
+
+void
+ao_radio_pa_on(void)
+{
+ ao_rfpa0133_set_power(ao_config.radio_amp);
+ ao_gpio_set(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 1);
+}
+
+void
+ao_radio_pa_off(void)
+{
+ ao_gpio_set(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 0);
+ ao_rfpa0133_set_power(0);
+}
+
+void
+ao_radio_pa_init(void)
+{
+ ao_enable_output(AO_PA_POWER_GPIO, AO_PA_POWER_PIN, AO_PA_POWER, 0);
+ ao_enable_output(AO_PA_GAIN_8_GPIO, AO_PA_GAIN_8_PIN, AO_PA_GAIN_8, 0);
+ ao_enable_output(AO_PA_GAIN_16_GPIO, AO_PA_GAIN_16_PIN, AO_PA_GAIN_16, 0);
+}
--- /dev/null
+/*
+ * Copyright © 2013 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.
+ */
+
+#ifndef _AO_RFPA0133_H_
+#define _AO_RFPA0133_H_
+
+void
+ao_rfpa0133_on(void);
+
+void
+ao_rfpa0133_off(void);
+
+void
+ao_rfpa0133_init(void);
+
+#endif /* _AO_RFPA0133_H_ */
--- /dev/null
+/*
+ * Copyright © 2013 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 "ao.h"
+#include "ao_sdcard.h"
+
+extern uint8_t ao_radio_mutex;
+
+#define ao_sdcard_get_slow() do { ao_mutex_get(&ao_radio_mutex); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_250kHz); } while (0)
+#define ao_sdcard_get() do { ao_mutex_get(&ao_radio_mutex); ao_spi_get(AO_SDCARD_SPI_BUS, AO_SPI_SPEED_FAST); } while (0)
+#define ao_sdcard_put() do { ao_spi_put(AO_SDCARD_SPI_BUS); ao_mutex_put(&ao_radio_mutex); } while (0)
+#define ao_sdcard_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_SDCARD_SPI_BUS)
+#define ao_sdcard_send(d,l) ao_spi_send((d), (l), AO_SDCARD_SPI_BUS)
+#define ao_sdcard_recv(d,l) ao_spi_recv((d), (l), AO_SDCARD_SPI_BUS)
+#define ao_sdcard_select() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,0)
+#define ao_sdcard_deselect() ao_gpio_set(AO_SDCARD_SPI_CS_PORT,AO_SDCARD_SPI_CS_PIN,AO_SDCARD_SPI_CS,1)
+
+/* Include SD card commands */
+#define SDCARD_DEBUG 0
+
+/* Spew SD tracing */
+#define SDCARD_TRACE 0
+
+/* Emit error and warning messages */
+#define SDCARD_WARN 0
+
+static uint8_t initialized;
+static uint8_t present;
+static uint8_t mutex;
+static enum ao_sdtype sdtype;
+
+#define ao_sdcard_lock() ao_mutex_get(&mutex)
+#define ao_sdcard_unlock() ao_mutex_put(&mutex)
+
+#if SDCARD_TRACE
+#define DBG(...) printf(__VA_ARGS__)
+#else
+#define DBG(...)
+#endif
+
+#if SDCARD_WARN
+#define WARN(...) printf(__VA_ARGS__)
+#else
+#define WARN(...)
+#endif
+
+#define later(x,y) ((int16_t) ((x) - (y)) >= 0)
+
+/*
+ * Wait while the card is busy. The card will return a stream of 0xff
+ * when it is ready to accept a command
+ */
+
+static uint8_t
+ao_sdcard_wait_busy(void)
+{
+ uint16_t timeout = ao_time() + SDCARD_BUSY_TIMEOUT;
+ uint8_t reply;
+ for (;;) {
+ ao_sdcard_recv(&reply, 1);
+ DBG("\t\twait busy %02x\n", reply);
+ if (reply == 0xff)
+ break;
+ if (later(ao_time(), timeout)) {
+ WARN("wait busy timeout\n");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * Send an SD command and await the status reply
+ */
+
+static uint8_t
+ao_sdcard_send_cmd(uint8_t cmd, uint32_t arg)
+{
+ uint8_t data[6];
+ uint8_t reply;
+ int i;
+ uint16_t timeout;
+
+ DBG ("\tsend_cmd %d arg %08x\n", cmd, arg);
+
+ /* Wait for the card to not be busy */
+ if (cmd != SDCARD_GO_IDLE_STATE) {
+ if (!ao_sdcard_wait_busy())
+ return SDCARD_STATUS_TIMEOUT;
+ }
+
+ data[0] = cmd & 0x3f | 0x40;
+ data[1] = arg >> 24;
+ data[2] = arg >> 16;
+ data[3] = arg >> 8;
+ data[4] = arg;
+ if (cmd == SDCARD_GO_IDLE_STATE)
+ data[5] = 0x95; /* Valid for 0 arg */
+ else if (cmd == SDCARD_SEND_IF_COND)
+ data[5] = 0x87; /* Valid for 0x1aa arg */
+ else
+ data[5] = 0xff; /* no CRC */
+ ao_sdcard_send(data, 6);
+
+ /* The first reply byte will be the status,
+ * which must have the high bit clear
+ */
+ timeout = ao_time() + SDCARD_CMD_TIMEOUT;
+ for (;;) {
+ ao_sdcard_recv(&reply, 1);
+ DBG ("\t\tgot byte %02x\n", reply);
+ if ((reply & 0x80) == 0)
+ break;
+ if (later(ao_time(), timeout)) {
+ WARN("send_cmd %02x timeout\n", cmd);
+ return SDCARD_STATUS_TIMEOUT;
+ }
+ }
+#if SDCARD_WARN
+ if (reply != SDCARD_STATUS_READY_STATE && reply != SDCARD_STATUS_IDLE_STATE)
+ WARN("send_cmd %d failed %02x\n", cmd, reply);
+#endif
+ return reply;
+}
+
+/*
+ * Retrieve any reply, discarding the trailing CRC byte
+ */
+static void
+ao_sdcard_recv_reply(uint8_t *reply, int len)
+{
+ uint8_t discard;
+
+ if (len)
+ ao_sdcard_recv(reply, len);
+ /* trailing byte */
+ ao_sdcard_recv(&discard, 1);
+}
+
+/*
+ * Switch to 'idle' state. This is used to get the card into SPI mode
+ */
+static uint8_t
+ao_sdcard_go_idle_state(void)
+{
+ uint8_t ret;
+
+ DBG ("go_idle_state\n");
+ ao_sdcard_select();
+ ret = ao_sdcard_send_cmd(SDCARD_GO_IDLE_STATE, 0);
+ ao_sdcard_recv_reply(NULL, 0);
+ ao_sdcard_deselect();
+ DBG ("\tgo_idle_state status %02x\n", ret);
+ return ret;
+}
+
+static uint8_t
+ao_sdcard_send_op_cond(void)
+{
+ uint8_t ret;
+
+ DBG ("send_op_cond\n");
+ ao_sdcard_select();
+ ret = ao_sdcard_send_cmd(SDCARD_SEND_OP_COND, 0);
+ ao_sdcard_recv_reply(NULL, 0);
+ ao_sdcard_deselect();
+ DBG ("\tsend_op_cond %02x\n", ret);
+ return ret;
+}
+
+static uint8_t
+ao_sdcard_send_if_cond(uint32_t arg, uint8_t send_if_cond_response[4])
+{
+ uint8_t ret;
+
+ DBG ("send_if_cond\n");
+ ao_sdcard_select();
+ ret = ao_sdcard_send_cmd(SDCARD_SEND_IF_COND, arg);
+ if (ret != SDCARD_STATUS_IDLE_STATE) {
+ DBG ("\tsend_if_cond failed %02x\n", ret);
+ return ret;
+ }
+ ao_sdcard_recv_reply(send_if_cond_response, 4);
+ DBG ("send_if_cond status %02x response %02x %02x %02x %02x\n",
+ ret,
+ send_if_cond_response[0],
+ send_if_cond_response[1],
+ send_if_cond_response[2],
+ send_if_cond_response[3]);
+ ao_sdcard_deselect();
+ return ret;
+}
+
+/*
+ * _ao_sdcard_send_status
+ *
+ * Get the 2-byte status value.
+ *
+ * Called from other functions with CS held low already,
+ * hence prefixing the name with '_'
+ */
+static uint16_t
+_ao_sdcard_send_status(void)
+{
+ uint8_t ret;
+ uint8_t extra;
+
+ DBG ("send_status\n");
+ ret = ao_sdcard_send_cmd(SDCARD_SEND_STATUS, 0);
+ ao_sdcard_recv_reply(&extra, 1);
+ if (ret != SDCARD_STATUS_READY_STATE)
+ DBG ("\tsend_if_cond failed %02x\n", ret);
+ return ret | (extra << 8);
+}
+
+/*
+ * ao_sdcard_set_blocklen
+ *
+ * Set the block length for future read and write commands
+ */
+static uint8_t
+ao_sdcard_set_blocklen(uint32_t blocklen)
+{
+ uint8_t ret;
+
+ DBG ("set_blocklen %d\n", blocklen);
+ ao_sdcard_select();
+ ret = ao_sdcard_send_cmd(SDCARD_SET_BLOCKLEN, blocklen);
+ ao_sdcard_recv_reply(NULL, 0);
+ ao_sdcard_deselect();
+ if (ret != SDCARD_STATUS_READY_STATE)
+ DBG ("\tsend_if_cond failed %02x\n", ret);
+ return ret;
+}
+
+/*
+ * _ao_sdcard_app_cmd
+ *
+ * Send the app command prefix
+ *
+ * Called with the CS held low, hence
+ * the '_' prefix
+ */
+static uint8_t
+_ao_sdcard_app_cmd(void)
+{
+ uint8_t ret;
+
+ DBG ("app_cmd\n");
+ ret = ao_sdcard_send_cmd(SDCARD_APP_CMD, 0);
+ ao_sdcard_recv_reply(NULL, 0);
+ DBG ("\tapp_cmd status %02x\n");
+ return ret;
+}
+
+static uint8_t
+ao_sdcard_app_send_op_cond(uint32_t arg)
+{
+ uint8_t ret;
+
+ DBG("send_op_comd\n");
+ ao_sdcard_select();
+ ret = _ao_sdcard_app_cmd();
+ if (ret != SDCARD_STATUS_IDLE_STATE)
+ goto bail;
+ ret = ao_sdcard_send_cmd(SDCARD_APP_SEND_OP_COMD, arg);
+ ao_sdcard_recv_reply(NULL, 0);
+bail:
+ ao_sdcard_deselect();
+ DBG ("\tapp_send_op_cond status %02x\n", ret);
+ return ret;
+}
+
+static uint8_t
+ao_sdcard_read_ocr(uint8_t read_ocr_response[4])
+{
+ uint8_t ret;
+
+ DBG ("read_ocr\n");
+ ao_sdcard_select();
+ ret = ao_sdcard_send_cmd(SDCARD_READ_OCR, 0);
+ if (ret != SDCARD_STATUS_READY_STATE)
+ DBG ("\tread_ocr failed %02x\n", ret);
+ else {
+ ao_sdcard_recv_reply(read_ocr_response, 4);
+ DBG ("\tread_ocr status %02x response %02x %02x %02x %02x\n", ret,
+ read_ocr_response[0], read_ocr_response[1],
+ read_ocr_response[2], read_ocr_response[3]);
+ }
+ ao_sdcard_deselect();
+ return ret;
+}
+
+/*
+ * Follow the flow-chart defined by the SD group to
+ * initialize the card and figure out what kind it is
+ */
+static void
+ao_sdcard_setup(void)
+{
+ int i;
+ uint8_t ret;
+ uint8_t response[10];
+
+ DBG ("Testing sdcard\n");
+
+ ao_sdcard_get_slow();
+ /*
+ * min 74 clocks with CS high
+ */
+ ao_sdcard_send_fixed(0xff, 10);
+
+ /* Reset the card and get it into SPI mode */
+ for (i = 0; i < SDCARD_IDLE_RETRY; i++) {
+ if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE)
+ break;
+ }
+ if (i == SDCARD_IDLE_RETRY)
+ goto bail;
+
+ /* Figure out what kind of card we have */
+ sdtype = ao_sdtype_unknown;
+
+ if (ao_sdcard_send_if_cond(0x1aa, response) == SDCARD_STATUS_IDLE_STATE) {
+ uint32_t arg = 0;
+ uint8_t sdver2 = 0;
+
+ /* Check for SD version 2 */
+ if ((response[2] & 0xf) == 1 && response[3] == 0xaa) {
+ arg = 0x40000000;
+ sdver2 = 1;
+ }
+
+ for (i = 0; i < SDCARD_OP_COND_RETRY; i++) {
+ ao_delay(AO_MS_TO_TICKS(10));
+ ret = ao_sdcard_app_send_op_cond(arg);
+ if (ret != SDCARD_STATUS_IDLE_STATE)
+ break;
+ }
+ if (ret != SDCARD_STATUS_READY_STATE) {
+ /* MMC */
+ for (i = 0; i < SDCARD_OP_COND_RETRY; i++) {
+ ao_delay(AO_MS_TO_TICKS(10));
+ ret = ao_sdcard_send_op_cond();
+ if (ret != SDCARD_STATUS_IDLE_STATE)
+ break;
+ }
+ if (ret != SDCARD_STATUS_READY_STATE)
+ goto bail;
+ sdtype = ao_sdtype_mmc3;
+ } else {
+ /* SD */
+ if (sdver2 != 0) {
+ ret = ao_sdcard_read_ocr(response);
+ if (ret != SDCARD_STATUS_READY_STATE)
+ goto bail;
+ if ((response[0] & 0xc0) == 0xc0)
+ sdtype = ao_sdtype_sd2block;
+ else
+ sdtype = ao_sdtype_sd2byte;
+ } else {
+ sdtype = ao_sdtype_sd1;
+ }
+ }
+
+ /* For everything but SDHC cards, set the block length */
+ if (sdtype != ao_sdtype_sd2block) {
+ ret = ao_sdcard_set_blocklen(512);
+ if (ret != SDCARD_STATUS_READY_STATE)
+ DBG ("set_blocklen failed, ignoring\n");
+ }
+ }
+
+ DBG ("SD card detected, type %d\n", sdtype);
+bail:
+ ao_sdcard_put();
+}
+
+static uint8_t
+_ao_sdcard_reset(void)
+{
+ int i;
+ uint8_t ret;
+ uint8_t response[10];
+
+ for (i = 0; i < SDCARD_IDLE_RETRY; i++) {
+ if (ao_sdcard_go_idle_state() == SDCARD_STATUS_IDLE_STATE)
+ break;
+ }
+ if (i == SDCARD_IDLE_RETRY) {
+ ret = 0x3f;
+ goto bail;
+ }
+
+ /* Follow the setup path to get the card out of idle state and
+ * up and running again
+ */
+ if (ao_sdcard_send_if_cond(0x1aa, response) == SDCARD_STATUS_IDLE_STATE) {
+ uint32_t arg = 0;
+ uint8_t sdver2 = 0;
+
+ /* Check for SD version 2 */
+ if ((response[2] & 0xf) == 1 && response[3] == 0xaa) {
+ arg = 0x40000000;
+ sdver2 = 1;
+ }
+
+ for (i = 0; i < SDCARD_IDLE_RETRY; i++) {
+ ret = ao_sdcard_app_send_op_cond(arg);
+ if (ret != SDCARD_STATUS_IDLE_STATE)
+ break;
+ }
+
+ if (ret != SDCARD_STATUS_READY_STATE) {
+ /* MMC */
+ for (i = 0; i < SDCARD_IDLE_RETRY; i++) {
+ ret = ao_sdcard_send_op_cond();
+ if (ret != SDCARD_STATUS_IDLE_STATE)
+ break;
+ }
+ if (ret != SDCARD_STATUS_READY_STATE)
+ goto bail;
+ }
+
+ /* For everything but SDHC cards, set the block length */
+ if (sdtype != ao_sdtype_sd2block) {
+ ret = ao_sdcard_set_blocklen(512);
+ if (ret != SDCARD_STATUS_READY_STATE)
+ DBG ("set_blocklen failed, ignoring\n");
+ }
+ }
+bail:
+ return ret;
+}
+
+/*
+ * The card will send 0xff until it is ready to send
+ * the data block at which point it will send the START_BLOCK
+ * marker followed by the data. This function waits while
+ * the card is sending 0xff
+ */
+static uint8_t
+ao_sdcard_wait_block_start(void)
+{
+ uint8_t v;
+ uint16_t timeout = ao_time() + SDCARD_BLOCK_TIMEOUT;
+
+ DBG ("\twait_block_start\n");
+ for (;;) {
+ ao_sdcard_recv(&v, 1);
+ DBG("\t\trecv %02x\n", v);
+ if (v != 0xff)
+ break;
+ if (later(ao_time(), timeout)) {
+ printf ("wait block start timeout\n");
+ return 0xff;
+ }
+ }
+ return v;
+}
+
+/*
+ * Read a block of 512 bytes from the card
+ */
+uint8_t
+ao_sdcard_read_block(uint32_t block, uint8_t *data)
+{
+ uint8_t ret;
+ uint8_t start_block;
+ uint8_t crc[2];
+ int tries;
+
+ ao_sdcard_lock();
+ if (!initialized) {
+ ao_sdcard_setup();
+ initialized = 1;
+ if (sdtype != ao_sdtype_unknown)
+ present = 1;
+ }
+ if (!present) {
+ ao_sdcard_unlock();
+ return 0;
+ }
+ DBG("read block %d\n", block);
+ if (sdtype != ao_sdtype_sd2block)
+ block <<= 9;
+
+ ao_sdcard_get();
+ for (tries = 0; tries < 10; tries++) {
+ ao_sdcard_select();
+
+ ret = ao_sdcard_send_cmd(SDCARD_READ_BLOCK, block);
+ ao_sdcard_recv_reply(NULL, 0);
+ if (ret != SDCARD_STATUS_READY_STATE) {
+ uint16_t status;
+ WARN ("read block command failed %d status %02x\n", block, ret);
+ status = _ao_sdcard_send_status();
+ WARN ("\tstatus now %04x\n", status);
+ goto bail;
+ }
+
+ ao_sdcard_send_fixed(0xff, 1);
+
+ /* Wait for the data start block marker */
+ start_block = ao_sdcard_wait_block_start();
+ if (start_block != SDCARD_DATA_START_BLOCK) {
+ WARN ("wait block start failed %02x\n", start_block);
+ ret = 0x3f;
+ goto bail;
+ }
+
+ ao_sdcard_recv(data, 512);
+ ao_sdcard_recv(crc, 2);
+ bail:
+ ao_sdcard_deselect();
+ if (ret == SDCARD_STATUS_READY_STATE)
+ break;
+ if (ret == SDCARD_STATUS_IDLE_STATE) {
+ ret = _ao_sdcard_reset();
+ if (ret != SDCARD_STATUS_READY_STATE)
+ break;
+ }
+ }
+ ao_sdcard_put();
+ ao_sdcard_unlock();
+
+#if SDCARD_WARN
+ if (ret != SDCARD_STATUS_READY_STATE)
+ WARN("read failed\n");
+ else if (tries)
+ WARN("took %d tries to read %d\n", tries + 1, block);
+#endif
+
+ DBG("read %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure");
+ return ret == SDCARD_STATUS_READY_STATE;
+}
+
+/*
+ * Write a block of 512 bytes to the card
+ */
+uint8_t
+ao_sdcard_write_block(uint32_t block, uint8_t *data)
+{
+ uint8_t ret;
+ uint8_t response[1];
+ uint8_t start_block[8];
+ uint16_t status;
+ static uint8_t check_data[512];
+ int i;
+ int tries;
+
+ ao_sdcard_lock();
+ if (!initialized) {
+ ao_sdcard_setup();
+ initialized = 1;
+ if (sdtype != ao_sdtype_unknown)
+ present = 1;
+ }
+ if (!present) {
+ ao_sdcard_unlock();
+ return 0;
+ }
+ DBG("write block %d\n", block);
+ if (sdtype != ao_sdtype_sd2block)
+ block <<= 9;
+
+ ao_sdcard_get();
+
+ for (tries = 0; tries < 10; tries++) {
+ ao_sdcard_select();
+
+ ret = ao_sdcard_send_cmd(SDCARD_WRITE_BLOCK, block);
+ ao_sdcard_recv_reply(NULL, 0);
+ if (ret != SDCARD_STATUS_READY_STATE)
+ goto bail;
+
+ /* Write a pad byte followed by the data start block marker */
+ start_block[0] = 0xff;
+ start_block[1] = SDCARD_DATA_START_BLOCK;
+ ao_sdcard_send(start_block, 2);
+
+ /* Send the data */
+ ao_sdcard_send(data, 512);
+
+ /* Fake the CRC */
+ ao_sdcard_send_fixed(0xff, 2);
+
+ /* See if the card liked the data */
+ ao_sdcard_recv(response, sizeof (response));
+ if ((response[0] & SDCARD_DATA_RES_MASK) != SDCARD_DATA_RES_ACCEPTED) {
+ int i;
+ WARN("Data not accepted, response");
+ for (i = 0; i < sizeof (response); i++)
+ WARN(" %02x", response[i]);
+ WARN("\n");
+ ret = 0x3f;
+ goto bail;
+ }
+
+ /* Wait for the bus to go idle (should be done with an interrupt?) */
+ if (!ao_sdcard_wait_busy()) {
+ ret = 0x3f;
+ goto bail;
+ }
+
+ /* Check the current status after the write completes */
+ status = _ao_sdcard_send_status();
+ if ((status & 0xff) != SDCARD_STATUS_READY_STATE) {
+ WARN ("send status after write %04x\n", status);
+ ret = status & 0xff;
+ goto bail;
+ }
+ bail:
+ ao_sdcard_deselect();
+ DBG("write %s\n", ret == SDCARD_STATUS_READY_STATE ? "success" : "failure");
+ if (ret == SDCARD_STATUS_READY_STATE)
+ break;
+ }
+ ao_sdcard_put();
+ ao_sdcard_unlock();
+ if (tries)
+ WARN("took %d tries to write %d\n", tries + 1, block);
+
+ return ret == SDCARD_STATUS_READY_STATE;
+}
+
+#if SDCARD_DEBUG
+static uint8_t test_data[512];
+
+static void
+ao_sdcard_test_read(void)
+{
+ int i;
+
+ ao_cmd_decimal();
+ if (ao_cmd_status != ao_cmd_success)
+ return;
+
+ for (i = 0; i < 100; i++) {
+ printf ("."); flush();
+ if (!ao_sdcard_read_block(ao_cmd_lex_u32+i, test_data)) {
+ printf ("read error %d\n", i);
+ return;
+ }
+ }
+ printf ("data:");
+ for (i = 0; i < 18; i++)
+ printf (" %02x", test_data[i]);
+ printf ("\n");
+}
+
+static void
+ao_sdcard_test_write(void)
+{
+ int i;
+ printf ("data:");
+ for (i = 0; i < 16; i++) {
+ test_data[i]++;
+ printf (" %02x", test_data[i]);
+ }
+ printf ("\n");
+ if (!ao_sdcard_write_block(1, test_data)) {
+ printf ("write error\n");
+ return;
+ }
+}
+
+static const struct ao_cmds ao_sdcard_cmds[] = {
+ { ao_sdcard_test_read, "x\0Test read" },
+ { ao_sdcard_test_write, "y\0Test read" },
+ { 0, NULL },
+};
+#endif
+
+void
+ao_sdcard_init(void)
+{
+ stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_SCK_PIN, STM_PUPDR_PULL_UP);
+ stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_MISO_PIN, STM_PUPDR_PULL_UP);
+ stm_pupdr_set(AO_SDCARD_SPI_PORT, AO_SDCARD_SPI_MOSI_PIN, STM_PUPDR_PULL_UP);
+ ao_spi_init_cs(AO_SDCARD_SPI_CS_PORT, (1 << AO_SDCARD_SPI_CS_PIN));
+#if SDCARD_DEBUG
+ ao_cmd_register(&ao_sdcard_cmds[0]);
+#endif
+}
--- /dev/null
+/*
+ * Copyright © 2013 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.
+ */
+
+#ifndef _AO_SDCARD_H_
+#define _AO_SDCARD_H_
+
+uint8_t
+ao_sdcard_read_block(uint32_t block, uint8_t *data);
+
+uint8_t
+ao_sdcard_write_block(uint32_t block, uint8_t *data);
+
+void
+ao_sdcard_init(void);
+
+/* Commands */
+#define SDCARD_GO_IDLE_STATE 0
+#define SDCARD_SEND_OP_COND 1
+#define SDCARD_SEND_IF_COND 8
+#define SDCARD_SEND_CSD 9
+#define SDCARD_SEND_CID 10
+#define SDCARD_SEND_STATUS 13
+#define SDCARD_SET_BLOCKLEN 16
+#define SDCARD_READ_BLOCK 17
+#define SDCARD_WRITE_BLOCK 24
+#define SDCARD_WRITE_MULTIPLE_BLOCK 25
+#define SDCARD_ERASE_WR_BLK_START 32
+#define SDCARD_ERASE_WR_BLK_END 33
+#define SDCARD_ERASE 38
+#define SDCARD_APP_CMD 55
+#define SDCARD_READ_OCR 58
+
+/* App commands */
+#define SDCARD_APP_SET_WR_BLK_ERASE_COUNT 23
+#define SDCARD_APP_SEND_OP_COMD 41
+
+/* Status */
+#define SDCARD_STATUS_READY_STATE 0x00
+#define SDCARD_STATUS_IDLE_STATE 0x01
+#define SDCARD_STATUS_ERASE_RESET 0x02
+#define SDCARD_STATUS_ILLEGAL_COMMAND 0x04
+#define SDCARD_STATUS_COM_CRC_ERROR 0x08
+#define SDCARD_STATUS_ERASE_SEQ_ERROR 0x10
+#define SDCARD_STATUS_ADDRESS_ERROR 0x20
+#define SDCARD_STATUS_PARAMETER_ERROR 0x40
+#define SDCARD_STATUS_TIMEOUT 0xff
+
+#define SDCARD_DATA_START_BLOCK 0xfe
+#define SDCARD_STOP_TRAN_TOKEN 0xfd
+#define SDCARD_WRITE_MULTIPLE_TOKEN 0xfc
+#define SDCARD_DATA_RES_MASK 0x1f
+#define SDCARD_DATA_RES_ACCEPTED 0x05
+
+#define SDCARD_CMD_TIMEOUT AO_MS_TO_TICKS(20)
+#define SDCARD_BUSY_TIMEOUT AO_MS_TO_TICKS(20)
+#define SDCARD_BLOCK_TIMEOUT AO_MS_TO_TICKS(200)
+#define SDCARD_IDLE_RETRY 10
+#define SDCARD_OP_COND_RETRY 10
+
+enum ao_sdtype {
+ ao_sdtype_unknown,
+ ao_sdtype_mmc3,
+ ao_sdtype_sd1,
+ ao_sdtype_sd2byte,
+ ao_sdtype_sd2block,
+};
+
+#endif /* _AO_SDCARD_H_ */
+++ /dev/null
-ao_product.h
-megametrum-*.elf
+++ /dev/null
-#
-# AltOS build
-#
-#
-
-include ../stm/Makefile.defs
-
-INC = \
- ao.h \
- ao_arch.h \
- ao_arch_funcs.h \
- ao_companion.h \
- ao_data.h \
- ao_sample.h \
- ao_pins.h \
- altitude-pa.h \
- ao_kalman.h \
- ao_product.h \
- ao_ms5607.h \
- ao_hmc5883.h \
- ao_mpu6000.h \
- ao_mma655x.h \
- ao_cc1120_CC1120.h \
- ao_profile.h \
- ao_task.h \
- ao_whiten.h \
- ao_sample_profile.h \
- ao_mpu.h \
- stm32l.h \
- Makefile
-
-#
-# Common AltOS sources
-#
-# ao_hmc5883.c
-
-#PROFILE=ao_profile.c
-#PROFILE_DEF=-DAO_PROFILE=1
-
-#SAMPLE_PROFILE=ao_sample_profile.c \
-# ao_sample_profile_timer.c
-#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
-
-#STACK_GUARD=ao_mpu_stm.c
-#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
-
-ALTOS_SRC = \
- ao_interrupt.c \
- ao_product.c \
- ao_romconfig.c \
- ao_cmd.c \
- ao_config.c \
- ao_task.c \
- ao_led.c \
- ao_stdio.c \
- ao_panic.c \
- ao_timer.c \
- ao_mutex.c \
- ao_serial_stm.c \
- ao_gps_skytraq.c \
- ao_gps_report_mega.c \
- ao_ignite.c \
- ao_freq.c \
- ao_dma_stm.c \
- ao_spi_stm.c \
- ao_cc1120.c \
- ao_fec_tx.c \
- ao_fec_rx.c \
- ao_data.c \
- ao_ms5607.c \
- ao_mma655x.c \
- ao_hmc5883.c \
- ao_adc_stm.c \
- ao_beep_stm.c \
- ao_storage.c \
- ao_m25.c \
- ao_usb_stm.c \
- ao_exti_stm.c \
- ao_report.c \
- ao_i2c_stm.c \
- ao_mpu6000.c \
- ao_convert_pa.c \
- ao_log.c \
- ao_log_mega.c \
- ao_sample.c \
- ao_kalman.c \
- ao_flight.c \
- ao_telemetry.c \
- ao_packet_slave.c \
- ao_packet.c \
- ao_companion.c \
- ao_pyro.c \
- ao_aprs.c \
- $(PROFILE) \
- $(SAMPLE_PROFILE) \
- $(STACK_GUARD)
-
-PRODUCT=MegaMetrum-v0.1
-PRODUCT_DEF=-DMEGAMETRUM
-IDPRODUCT=0x0023
-
-CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
-
-PROGNAME=megametrum-v0.1
-PROG=$(PROGNAME)-$(VERSION).elf
-
-SRC=$(ALTOS_SRC) ao_megametrum.c
-OBJ=$(SRC:.c=.o)
-
-all: $(PROG)
-
-$(PROG): Makefile $(OBJ) altos.ld
- $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
-
-../altitude-pa.h: make-altitude-pa
- nickle $< > $@
-
-$(OBJ): $(INC)
-
-ao_product.h: ao-make-product.5c ../Version
- $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
-
-distclean: clean
-
-clean:
- rm -f *.o $(PROGNAME)-*.elf
- rm -f ao_product.h
-
-install:
-
-uninstall:
+++ /dev/null
-/*
- * Copyright © 2011 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 <ao.h>
-#include <ao_hmc5883.h>
-#include <ao_mpu6000.h>
-#include <ao_mma655x.h>
-#include <ao_log.h>
-#include <ao_exti.h>
-#include <ao_packet.h>
-#include <ao_companion.h>
-#include <ao_profile.h>
-#if HAS_SAMPLE_PROFILE
-#include <ao_sample_profile.h>
-#endif
-#include <ao_pyro.h>
-#if HAS_STACK_GUARD
-#include <ao_mpu.h>
-#endif
-
-int
-main(void)
-{
- ao_clock_init();
-
-#if HAS_STACK_GUARD
- ao_mpu_init();
-#endif
-
- ao_task_init();
- ao_serial_init();
- ao_led_init(LEDS_AVAILABLE);
- ao_led_on(AO_LED_GREEN);
- ao_timer_init();
-
- ao_i2c_init();
- ao_spi_init();
- ao_dma_init();
- ao_exti_init();
-
- ao_adc_init();
-#if HAS_BEEP
- ao_beep_init();
-#endif
- ao_cmd_init();
-
-#if HAS_MS5607
- ao_ms5607_init();
-#endif
-#if HAS_HMC5883
- ao_hmc5883_init();
-#endif
-#if HAS_MPU6000
- ao_mpu6000_init();
-#endif
-#if HAS_MMA655X
- ao_mma655x_init();
-#endif
-
- ao_storage_init();
-
- ao_flight_init();
- ao_log_init();
- ao_report_init();
-
- ao_usb_init();
- ao_gps_init();
- ao_gps_report_mega_init();
- ao_telemetry_init();
- ao_radio_init();
- ao_packet_slave_init(FALSE);
- ao_igniter_init();
- ao_companion_init();
- ao_pyro_init();
-
- ao_config_init();
-#if AO_PROFILE
- ao_profile_init();
-#endif
-#if HAS_SAMPLE_PROFILE
- ao_sample_profile_init();
-#endif
-
- ao_start_scheduler();
- return 0;
-}
+++ /dev/null
-/*
- * Copyright © 2012 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.
- */
-
-#ifndef _AO_PINS_H_
-#define _AO_PINS_H_
-
-#define HAS_TASK_QUEUE 1
-
-/* 8MHz High speed external crystal */
-#define AO_HSE 8000000
-
-/* PLLVCO = 96MHz (so that USB will work) */
-#define AO_PLLMUL 12
-#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
-
-/* SYSCLK = 32MHz (no need to go faster than CPU) */
-#define AO_PLLDIV 3
-#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
-
-/* HCLK = 32MHz (CPU clock) */
-#define AO_AHB_PRESCALER 1
-#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
-
-/* Run APB1 at 16MHz (HCLK/2) */
-#define AO_APB1_PRESCALER 2
-#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
-
-/* Run APB2 at 16MHz (HCLK/2) */
-#define AO_APB2_PRESCALER 2
-#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
-
-#define HAS_SERIAL_1 1
-#define USE_SERIAL_1_STDIN 0
-#define SERIAL_1_PB6_PB7 0
-#define SERIAL_1_PA9_PA10 1
-
-#define HAS_SERIAL_2 0
-#define USE_SERIAL_2_STDIN 0
-#define SERIAL_2_PA2_PA3 0
-#define SERIAL_2_PD5_PD6 0
-
-#define HAS_SERIAL_3 1
-#define USE_SERIAL_3_STDIN 0
-#define SERIAL_3_PB10_PB11 0
-#define SERIAL_3_PC10_PC11 1
-#define SERIAL_3_PD8_PD9 0
-
-#define ao_gps_getchar ao_serial3_getchar
-#define ao_gps_putchar ao_serial3_putchar
-#define ao_gps_set_speed ao_serial3_set_speed
-#define ao_gps_fifo (ao_stm_usart3.rx_fifo)
-
-#define HAS_EEPROM 1
-#define USE_INTERNAL_FLASH 0
-#define HAS_USB 1
-#define HAS_BEEP 1
-#define HAS_RADIO 1
-#define HAS_TELEMETRY 1
-#define HAS_APRS 1
-
-#define HAS_SPI_1 1
-#define SPI_1_PA5_PA6_PA7 1 /* Barometer */
-#define SPI_1_PB3_PB4_PB5 0
-#define SPI_1_PE13_PE14_PE15 1 /* Accelerometer */
-#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
-
-#define HAS_SPI_2 1
-#define SPI_2_PB13_PB14_PB15 1 /* Flash, Companion */
-#define SPI_2_PD1_PD3_PD4 0
-#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
-
-#define SPI_2_PORT (&stm_gpiob)
-#define SPI_2_SCK_PIN 13
-#define SPI_2_MISO_PIN 14
-#define SPI_2_MOSI_PIN 15
-
-#define HAS_I2C_1 1
-#define I2C_1_PB8_PB9 1
-
-#define HAS_I2C_2 1
-#define I2C_2_PB10_PB11 1
-
-#define PACKET_HAS_SLAVE 1
-#define PACKET_HAS_MASTER 0
-
-#define LOW_LEVEL_DEBUG 0
-
-#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOCEN
-#define LED_PORT (&stm_gpioc)
-#define LED_PIN_RED 8
-#define LED_PIN_GREEN 9
-#define AO_LED_RED (1 << LED_PIN_RED)
-#define AO_LED_GREEN (1 << LED_PIN_GREEN)
-
-#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
-
-#define HAS_GPS 1
-#define HAS_FLIGHT 1
-#define HAS_ADC 1
-#define HAS_LOG 1
-
-/*
- * Igniter
- */
-
-#define HAS_IGNITE 1
-#define HAS_IGNITE_REPORT 1
-
-#define AO_SENSE_DROGUE(p) ((p)->adc.sense[0])
-#define AO_SENSE_MAIN(p) ((p)->adc.sense[1])
-#define AO_IGNITER_CLOSED 400
-#define AO_IGNITER_OPEN 60
-
-#define AO_IGNITER_DROGUE_PORT (&stm_gpiod)
-#define AO_IGNITER_DROGUE_PIN 6
-
-#define AO_IGNITER_MAIN_PORT (&stm_gpiod)
-#define AO_IGNITER_MAIN_PIN 7
-
-#define AO_PYRO_PORT_0 (&stm_gpiob)
-#define AO_PYRO_PIN_0 5
-
-#define AO_PYRO_PORT_1 (&stm_gpioe)
-#define AO_PYRO_PIN_1 4
-
-#define AO_PYRO_PORT_2 (&stm_gpioe)
-#define AO_PYRO_PIN_2 6
-
-#define AO_PYRO_PORT_3 (&stm_gpioe)
-#define AO_PYRO_PIN_3 5
-
-/* Number of general purpose pyro channels available */
-#define AO_PYRO_NUM 4
-
-#define AO_IGNITER_SET_DROGUE(v) stm_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, v)
-#define AO_IGNITER_SET_MAIN(v) stm_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, v)
-
-/*
- * ADC
- */
-#define AO_DATA_RING 32
-#define AO_ADC_NUM_SENSE 6
-
-struct ao_adc {
- int16_t sense[AO_ADC_NUM_SENSE];
- int16_t v_batt;
- int16_t v_pbatt;
- int16_t accel_ref;
- int16_t accel;
- int16_t temp;
-};
-
-#define AO_ADC_SENSE_A 0
-#define AO_ADC_SENSE_A_PORT (&stm_gpioa)
-#define AO_ADC_SENSE_A_PIN 0
-
-#define AO_ADC_SENSE_B 1
-#define AO_ADC_SENSE_B_PORT (&stm_gpioa)
-#define AO_ADC_SENSE_B_PIN 1
-
-#define AO_ADC_SENSE_C 2
-#define AO_ADC_SENSE_C_PORT (&stm_gpioa)
-#define AO_ADC_SENSE_C_PIN 2
-
-#define AO_ADC_SENSE_D 3
-#define AO_ADC_SENSE_D_PORT (&stm_gpioa)
-#define AO_ADC_SENSE_D_PIN 3
-
-#define AO_ADC_SENSE_E 4
-#define AO_ADC_SENSE_E_PORT (&stm_gpioa)
-#define AO_ADC_SENSE_E_PIN 4
-
-#define AO_ADC_SENSE_F 22
-#define AO_ADC_SENSE_F_PORT (&stm_gpioe)
-#define AO_ADC_SENSE_F_PIN 7
-
-#define AO_ADC_V_BATT 8
-#define AO_ADC_V_BATT_PORT (&stm_gpiob)
-#define AO_ADC_V_BATT_PIN 0
-
-#define AO_ADC_V_PBATT 9
-#define AO_ADC_V_PBATT_PORT (&stm_gpiob)
-#define AO_ADC_V_PBATT_PIN 1
-
-#define AO_ADC_ACCEL_REF 10
-#define AO_ADC_ACCEL_REF_PORT (&stm_gpioc)
-#define AO_ADC_ACCEL_REF_PIN 0
-
-#define AO_ADC_ACCEL 11
-#define AO_ADC_ACCEL_PORT (&stm_gpioc)
-#define AO_ADC_ACCEL_PIN 1
-
-#define AO_ADC_TEMP 16
-
-#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN) | \
- (1 << STM_RCC_AHBENR_GPIOEEN) | \
- (1 << STM_RCC_AHBENR_GPIOBEN) | \
- (1 << STM_RCC_AHBENR_GPIOCEN))
-
-#define AO_NUM_ADC_PIN (AO_ADC_NUM_SENSE + 4)
-
-#define AO_ADC_PIN0_PORT AO_ADC_SENSE_A_PORT
-#define AO_ADC_PIN0_PIN AO_ADC_SENSE_A_PIN
-#define AO_ADC_PIN1_PORT AO_ADC_SENSE_B_PORT
-#define AO_ADC_PIN1_PIN AO_ADC_SENSE_B_PIN
-#define AO_ADC_PIN2_PORT AO_ADC_SENSE_C_PORT
-#define AO_ADC_PIN2_PIN AO_ADC_SENSE_C_PIN
-#define AO_ADC_PIN3_PORT AO_ADC_SENSE_D_PORT
-#define AO_ADC_PIN3_PIN AO_ADC_SENSE_D_PIN
-#define AO_ADC_PIN4_PORT AO_ADC_SENSE_E_PORT
-#define AO_ADC_PIN4_PIN AO_ADC_SENSE_E_PIN
-#define AO_ADC_PIN5_PORT AO_ADC_SENSE_F_PORT
-#define AO_ADC_PIN5_PIN AO_ADC_SENSE_F_PIN
-#define AO_ADC_PIN6_PORT AO_ADC_V_BATT_PORT
-#define AO_ADC_PIN6_PIN AO_ADC_V_BATT_PIN
-#define AO_ADC_PIN7_PORT AO_ADC_V_PBATT_PORT
-#define AO_ADC_PIN7_PIN AO_ADC_V_PBATT_PIN
-#define AO_ADC_PIN8_PORT AO_ADC_ACCEL_REF_PORT
-#define AO_ADC_PIN8_PIN AO_ADC_ACCEL_REF_PIN
-#define AO_ADC_PIN9_PORT AO_ADC_ACCEL_PORT
-#define AO_ADC_PIN9_PIN AO_ADC_ACCEL_PIN
-
-#define AO_NUM_ADC (AO_ADC_NUM_SENSE + 5)
-
-#define AO_ADC_SQ1 AO_ADC_SENSE_A
-#define AO_ADC_SQ2 AO_ADC_SENSE_B
-#define AO_ADC_SQ3 AO_ADC_SENSE_C
-#define AO_ADC_SQ4 AO_ADC_SENSE_D
-#define AO_ADC_SQ5 AO_ADC_SENSE_E
-#define AO_ADC_SQ6 AO_ADC_SENSE_F
-#define AO_ADC_SQ7 AO_ADC_V_BATT
-#define AO_ADC_SQ8 AO_ADC_V_PBATT
-#define AO_ADC_SQ9 AO_ADC_ACCEL_REF
-#define AO_ADC_SQ10 AO_ADC_ACCEL
-#define AO_ADC_SQ11 AO_ADC_TEMP
-
-/*
- * Pressure sensor settings
- */
-#define HAS_MS5607 1
-#define HAS_MS5611 0
-#define AO_MS5607_PRIVATE_PINS 1
-#define AO_MS5607_CS_PORT (&stm_gpioc)
-#define AO_MS5607_CS_PIN 4
-#define AO_MS5607_CS_MASK (1 << AO_MS5607_CS)
-#define AO_MS5607_MISO_PORT (&stm_gpioa)
-#define AO_MS5607_MISO_PIN 6
-#define AO_MS5607_MISO_MASK (1 << AO_MS5607_MISO)
-#define AO_MS5607_SPI_INDEX AO_SPI_1_PA5_PA6_PA7
-
-/*
- * SPI Flash memory
- */
-
-#define M25_MAX_CHIPS 1
-#define AO_M25_SPI_CS_PORT (&stm_gpiod)
-#define AO_M25_SPI_CS_MASK (1 << 3)
-#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15
-
-/*
- * Radio (cc1120)
- */
-
-/* gets pretty close to 434.550 */
-
-#define AO_RADIO_CAL_DEFAULT 0x6ca333
-
-#define AO_FEC_DEBUG 0
-#define AO_CC1120_SPI_CS_PORT (&stm_gpioc)
-#define AO_CC1120_SPI_CS_PIN 5
-#define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15
-#define AO_CC1120_SPI stm_spi2
-
-#define AO_CC1120_INT_PORT (&stm_gpioc)
-#define AO_CC1120_INT_PIN 14
-#define AO_CC1120_MCU_WAKEUP_PORT (&stm_gpioc)
-#define AO_CC1120_MCU_WAKEUP_PIN (0)
-
-#define AO_CC1120_INT_GPIO 2
-#define AO_CC1120_INT_GPIO_IOCFG CC1120_IOCFG2
-
-#define AO_CC1120_MARC_GPIO 3
-#define AO_CC1120_MARC_GPIO_IOCFG CC1120_IOCFG3
-
-
-#define HAS_BOOT_RADIO 0
-
-/*
- * Mag sensor (hmc5883)
- */
-
-#define HAS_HMC5883 0
-#define AO_HMC5883_INT_PORT (&stm_gpioc)
-#define AO_HMC5883_INT_PIN 12
-#define AO_HMC5883_I2C_INDEX STM_I2C_INDEX(1)
-
-/*
- * mpu6000
- */
-
-#define HAS_MPU6000 1
-#define AO_MPU6000_INT_PORT (&stm_gpioc)
-#define AO_MPU6000_INT_PIN 13
-#define AO_MPU6000_I2C_INDEX STM_I2C_INDEX(1)
-
-#define HAS_HIGHG_ACCEL 0
-
-/*
- * mma655x
- */
-
-#define HAS_MMA655X 1
-#define AO_MMA655X_SPI_INDEX AO_SPI_1_PE13_PE14_PE15
-#define AO_MMA655X_CS_PORT (&stm_gpiod)
-#define AO_MMA655X_CS_PIN 4
-
-#define NUM_CMDS 16
-
-/*
- * Companion
- */
-
-#define AO_COMPANION_CS_PORT (&stm_gpiod)
-#define AO_COMPANION_CS_PIN (0)
-#define AO_COMPANION_SPI_BUS AO_SPI_2_PB13_PB14_PB15
-
-/*
- * Monitor
- */
-
-#define HAS_MONITOR 0
-#define LEGACY_MONITOR 0
-#define HAS_MONITOR_PUT 1
-#define AO_MONITOR_LED 0
-#define HAS_RSSI 0
-
-/*
- * Profiling Viterbi decoding
- */
-
-#ifndef AO_PROFILE
-#define AO_PROFILE 0
-#endif
-
-#endif /* _AO_PINS_H_ */
+++ /dev/null
-ST discovery card pins
-
-1 AIN-1
-2 JTCK
-3 GND
-4 JTMS
-5 NRST
-6 SWO
-
-MegaMetrum v0.1 misc connector
-
-1 GND
-2 reset_n
-3 boot0
-4 tx1
-5 rx1
-6 +3.3V
-7 GND
-8 jtms
-9 jtck
-10 jtdi
-11 jtdo
-12 jntrst
-13 sda2
-14 scl2
-15 pe1
-16 pe0
-
-For debugging:
-
- ST MM v0.1
-JTCK 2 9
-GND 3 7
-JTMS 4 8
-NRST 5 2
-
-Altus Metrum STM32L standard debug connector (4 pin MicoMaTch):
-
- TL ST
-GND 1 3
-NRST 2 5
-SWDIO 3 4
-SWCLK 4 2
-
-MegaAccel:
-
-Jumpers
-PC0 (pin15) (blue) PE0 (pin97) accel_ref (debug 16)
-PC1 (pin16) (green) PE1 (pin98) accel (debug 15)
+++ /dev/null
-#
-# TeleBT build file
-#
-# Define TELEBT_VER, TELEBT_DEF, TELEBT_INC and TELEBT_SRC
-# and include this file
-
-vpath %.c ..:../core:../cc1111:../drivers:../product
-vpath %.h ..:../core:../cc1111:../drivers:../product
-vpath ao-make-product.5c ../util
-
-ifndef VERSION
-include ../Version
-endif
-
-INC = \
- ao.h \
- ao_pins.h \
- ao_arch.h \
- ao_arch_funcs.h \
- cc1111.h \
- ao_product.h \
- $(TELEBT_INC)
-
-CORE_SRC = \
- ao_cmd.c \
- ao_config.c \
- ao_gps_print.c \
- ao_monitor.c \
- ao_mutex.c \
- ao_panic.c \
- ao_state.c \
- ao_stdio.c \
- ao_task.c \
- ao_freq.c
-
-CC1111_SRC = \
- ao_dbg.c \
- ao_dma.c \
- ao_led.c \
- ao_packet.c \
- ao_packet_master.c \
- ao_radio.c \
- ao_romconfig.c \
- ao_serial.c \
- ao_string.c \
- ao_timer.c \
- ao_usb.c \
- _bp.c
-
-DRIVER_SRC = \
- ao_btm.c
-
-PRODUCT_SRC = \
- ao_telebt.c
-
-SRC = \
- $(CORE_SRC) \
- $(CC1111_SRC) \
- $(DRIVER_SRC) \
- $(PRODUCT_SRC) \
- $(TELEBT_SRC)
-
-PROGNAME = telebt-v$(TELEBT_VER)
-PROG = $(PROGNAME)-$(VERSION).ihx
-PRODUCT=TeleBT-v$(TELEBT_VER)
-PRODUCT_DEF=-DTELEBT_V_$(TELEBT_DEF)
-IDPRODUCT=0x000e
-
-include ../cc1111/Makefile.cc1111
-
-NICKLE=nickle
-CHECK_STACK=sh ../util/check-stack
-
-V=0
-# The user has explicitly enabled quiet compilation.
-ifeq ($(V),0)
-quiet = @printf " $1 $2 $@\n"; $($1)
-endif
-# Otherwise, print the full command line.
-quiet ?= $($1)
-
-all: $(PROG)
-
-$(PROG): $(REL) Makefile
- $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
- $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
-
-ao_product.h: ao-make-product.5c ../Version
- $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
-
-distclean: clean
-
-clean: clean-cc1111
-
-install:
-
-uninstall:
+++ /dev/null
-/*
- * Copyright © 2011 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 "ao.h"
-
-#if HAS_LOG
-__code uint8_t ao_log_format = AO_LOG_FORMAT_NONE; /* until we actually log stuff */
-#endif
-
-void
-main(void)
-{
- ao_clock_init();
-
- /* Turn on the LED until the system is stable */
- ao_led_init(LEDS_AVAILABLE);
- ao_led_on(AO_LED_RED);
-
- ao_task_init();
-
- ao_timer_init();
-#if HAS_BEEP
- ao_beep_init();
-#endif
- ao_cmd_init();
-#if HAS_EEPROM
- ao_spi_init();
- ao_storage_init();
-#endif
- ao_usb_init();
- ao_monitor_init();
-#if HAS_LOG
- ao_report_init();
-#endif
- ao_radio_init();
- ao_packet_master_init();
- ao_btm_init();
-#if HAS_LOG
- ao_log_single_init();
-#endif
-#if HAS_DBG
- ao_dbg_init();
-#endif
-#if HAS_AES
- ao_aes_init();
- ao_radio_cmac_init();
-#endif
- ao_config_init();
- ao_start_scheduler();
-}
CC=arm-none-eabi-gcc
OBJCOPY=arm-none-eabi-objcopy
-PDCLIB=/home/keithp/sat
-C_LIB=$(PDCLIB)/lib/pdclib.a
+PDCLIB=/opt/cortex
+C_LIB=$(PDCLIB)/lib/pdclib-cortex-m3.a
C_INC=-I$(PDCLIB)/include
DEF_CFLAGS=-g -std=gnu99 -Os -mlittle-endian -mthumb -ffreestanding -nostdlib -I. -I../../src/stm $(C_INC)
objcopy -O ihex $*.elf $@
CC=arm-none-eabi-gcc
-SAT=/home/keithp/sat
-SAT_CLIB=$(SAT)/lib/pdclib.a
+SAT=/opt/cortex
+SAT_CLIB=$(SAT)/lib/pdclib-cortex-m3.a
SAT_CFLAGS=-I$(SAT)/include
ifndef VERSION
static void
ao_intflash_unlock(void)
{
+ /* Disable backup write protection */
+ stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
+
/* Unlock Data EEPROM and FLASH_PECR register */
stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY1;
stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY2;
+ if (stm_flash.pecr & (1 << STM_FLASH_PECR_PELOCK))
+ printf ("eeprom unlock failed\n");
+
/* Configure the FTDW bit (FLASH_PECR[8]) to execute
* word write, whatever the previous value of the word
* being written to
(0 << STM_FLASH_PECR_EOPIE) |
(0 << STM_FLASH_PECR_FPRG) |
(0 << STM_FLASH_PECR_ERASE) |
- (0 << STM_FLASH_PECR_FTDW) |
- (1 << STM_FLASH_PECR_DATA) |
+ (1 << STM_FLASH_PECR_FTDW) |
+ (0 << STM_FLASH_PECR_DATA) |
(0 << STM_FLASH_PECR_PROG) |
(0 << STM_FLASH_PECR_OPTLOCK) |
(0 << STM_FLASH_PECR_PRGLOCK) |
addr = (uint32_t *) (stm_eeprom + pos);
- /* Erase previous word */
- *addr = 0;
+ /* Write a word to a valid address in the data EEPROM */
+ *addr = w;
ao_intflash_wait();
-
- if (w) {
- /* Write a word to a valid address in the data EEPROM */
- *addr = w;
- ao_intflash_wait();
- }
}
static void
uint8_t ao_i2c_mutex[STM_NUM_I2C];
# define I2C_HIGH_SLOW 5000 /* ns, 100kHz clock */
-#ifdef MEGAMETRUM
+#ifdef TELEMEGA
# define I2C_HIGH_FAST 2000 /* ns, 167kHz clock */
#else
# define I2C_HIGH_FAST 1000 /* ns, 333kHz clock */
uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
+ spi_dev_null = 0xff;
+
/* Set up transmit DMA to make the SPI hardware actually run */
ao_dma_set_transfer(mosi_dma_index,
&stm_spi->dr,
#include "ao.h"
#include <ao_task.h>
-volatile __data AO_TICK_TYPE ao_tick_count;
+volatile AO_TICK_TYPE ao_tick_count;
-uint16_t ao_time(void)
+AO_TICK_TYPE
+ao_time(void)
{
- uint16_t v;
- ao_arch_critical(
- v = ao_tick_count;
- );
- return v;
+ return ao_tick_count;
}
#if AO_DATA_ALL
+++ /dev/null
-telebt-*
-ao_product.h
+++ /dev/null
---directory=..
+++ /dev/null
-#
-# TeleBT v0.0 build
-#
-
-TELEBT_VER=0.0
-TELEBT_DEF=0_0
-
-include ../product/Makefile.telebt
-
+++ /dev/null
-telebt-*
-ao_product.h
+++ /dev/null
---directory=../cc1111:../product:../core:../drivers:.
-
+++ /dev/null
-#
-# TeleBT v0.1 build
-#
-
-TELEBT_VER=0.1
-TELEBT_DEF=0_1
-
-TELEBT_INC = \
- ao_25lc1024.h
-
-TELEBT_SRC = \
- ao_beep.c \
- ao_log_single.c \
- ao_log_telem.c \
- ao_report.c \
- ao_spi.c \
- ao_storage.c \
- ao_m25.c
-
-include ../product/Makefile.telebt
-
--- /dev/null
+telebt-*
+ao_product.h
--- /dev/null
+--directory=../cc1111:../product:../core:../drivers:.
+
--- /dev/null
+#
+# TeleBT build file
+#
+
+TELEBT_VER=1.0
+TELEBT_DEF=1_0
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+ ao.h \
+ ao_pins.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ cc1111.h \
+ ao_product.h
+
+CORE_SRC = \
+ ao_cmd.c \
+ ao_config.c \
+ ao_gps_print.c \
+ ao_monitor.c \
+ ao_mutex.c \
+ ao_panic.c \
+ ao_state.c \
+ ao_stdio.c \
+ ao_task.c \
+ ao_freq.c
+
+CC1111_SRC = \
+ ao_dbg.c \
+ ao_adc.c \
+ ao_dma.c \
+ ao_led.c \
+ ao_packet.c \
+ ao_packet_master.c \
+ ao_radio.c \
+ ao_romconfig.c \
+ ao_serial.c \
+ ao_string.c \
+ ao_timer.c \
+ ao_usb.c \
+ _bp.c
+
+DRIVER_SRC = \
+ ao_btm.c
+
+PRODUCT_SRC = \
+ ao_telebt.c
+
+SRC = \
+ $(CORE_SRC) \
+ $(CC1111_SRC) \
+ $(DRIVER_SRC) \
+ $(PRODUCT_SRC)
+
+PROGNAME = telebt-v$(TELEBT_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleBT-v$(TELEBT_VER)
+PRODUCT_DEF=-DTELEBT_V_$(TELEBT_DEF)
+IDPRODUCT=0x000e
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf " $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: $(PROG)
+
+$(PROG): $(REL) Makefile
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+ $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
--- /dev/null
+/*
+ * Copyright © 2010 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_RADIO 1
+#define HAS_FLIGHT 0
+#define HAS_USB 1
+#define HAS_BEEP 0
+#define HAS_SERIAL_1 1
+#define HAS_SERIAL_1_ALT_1 1
+#define HAS_SERIAL_1_ALT_2 0
+#define HAS_SERIAL_1_HW_FLOW 1
+#define USE_SERIAL_1_STDIN 1
+#define DELAY_SERIAL_1_STDIN 1
+#define HAS_DBG 1
+#define HAS_EEPROM 0
+#define HAS_LOG 0
+#define USE_INTERNAL_FLASH 0
+#define HAS_BTM 1
+#define DBG_ON_P1 1
+#define DBG_ON_P0 0
+#define PACKET_HAS_MASTER 1
+#define PACKET_HAS_SLAVE 0
+#define AO_LED_RED 1
+#define AO_LED_BLUE 2
+#define LEDS_AVAILABLE (AO_LED_RED|AO_LED_BLUE)
+#define AO_MONITOR_LED AO_LED_RED
+#define AO_BT_LED AO_LED_BLUE
+#define BT_LINK_ON_P2 0
+#define BT_LINK_ON_P1 1
+#define BT_LINK_PIN_INDEX 7
+#define BT_LINK_PIN P1_7
+#define HAS_MONITOR 1
+#define LEGACY_MONITOR 0
+
+#define HAS_ADC 1
+#define AO_PAD_ADC_BATT 0
+#define AO_ADC_PINS (1 << AO_PAD_ADC_BATT)
+
+struct ao_adc {
+ int16_t batt;
+};
+
+#define AO_ADC_DUMP(p) \
+ printf ("tick: %5u batt %5d\n", \
+ (p)->tick, \
+ (p)->adc.batt)
+
+#if DBG_ON_P1
+
+ #define DBG_CLOCK (1 << 4) /* mi0 */
+ #define DBG_DATA (1 << 5) /* mo0 */
+ #define DBG_RESET_N (1 << 3) /* c0 */
+
+ #define DBG_CLOCK_PIN (P1_4)
+ #define DBG_DATA_PIN (P1_5)
+ #define DBG_RESET_N_PIN (P1_3)
+
+ #define DBG_PORT_NUM 1
+ #define DBG_PORT P1
+ #define DBG_PORT_SEL P1SEL
+ #define DBG_PORT_INP P1INP
+ #define DBG_PORT_DIR P1DIR
+
+#endif /* DBG_ON_P1 */
+
+#if DBG_ON_P0
+
+ #define DBG_CLOCK (1 << 3)
+ #define DBG_DATA (1 << 4)
+ #define DBG_RESET_N (1 << 5)
+
+ #define DBG_CLOCK_PIN (P0_3)
+ #define DBG_DATA_PIN (P0_4)
+ #define DBG_RESET_N_PIN (P0_5)
+
+ #define DBG_PORT_NUM 0
+ #define DBG_PORT P0
+ #define DBG_PORT_SEL P0SEL
+ #define DBG_PORT_INP P0INP
+ #define DBG_PORT_DIR P0DIR
+
+#endif /* DBG_ON_P0 */
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2011 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 "ao.h"
+
+void
+main(void)
+{
+ ao_clock_init();
+
+ /* Turn on the LED until the system is stable */
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_RED);
+
+ ao_task_init();
+
+ ao_timer_init();
+ ao_cmd_init();
+ ao_usb_init();
+ ao_monitor_init();
+ ao_radio_init();
+ ao_packet_master_init();
+ ao_adc_init();
+ ao_btm_init();
+#if HAS_DBG
+ ao_dbg_init();
+#endif
+ ao_config_init();
+ ao_start_scheduler();
+}
--- /dev/null
+ao_product.h
+telegps-*.elf
--- /dev/null
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_pins.h \
+ ao_product.h \
+ ao_task.h \
+ ao_whiten.h \
+ ao_cc115l.h \
+ ao_fec.h \
+ stm32l.h \
+ ao_sdcard.h \
+ ao_bufio.h \
+ ao_fat.h \
+ Makefile
+
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+# ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_serial_stm.c \
+ ao_gps_skytraq.c \
+ ao_cc115l.c \
+ ao_fec_tx.c \
+ ao_rfpa0133.c \
+ ao_aprs.c \
+ ao_storage.c \
+ ao_eeprom_stm.c \
+ ao_sdcard.c \
+ ao_bufio.c \
+ ao_fat.c \
+ ao_log_fat.c \
+ ao_gps_report_mega.c \
+ ao_telemetry.c \
+ $(SAMPLE_PROFILE)
+
+PRODUCT=TeleGPS-v0.1
+PRODUCT_DEF=-DTELEGPS
+IDPRODUCT=0x0025
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
+
+PROGNAME=telegps-v0.1
+PROG=$(PROGNAME)-$(VERSION).elf
+
+SRC=$(ALTOS_SRC) ao_telegps.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf
+ rm -f ao_product.h
+
+install:
+
+uninstall:
--- /dev/null
+/*
+ * Copyright © 2012 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_TASK_QUEUE 1
+
+/* 8MHz High speed external crystal */
+#define AO_HSE 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV 3
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER 2
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER 2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1 1
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PB6_PB7 1
+#define SERIAL_1_PA9_PA10 0
+
+#define HAS_SERIAL_2 1
+#define USE_SERIAL_2_STDIN 0
+#define SERIAL_2_PA2_PA3 1
+#define SERIAL_2_PD5_PD6 0
+
+#define HAS_SERIAL_3 0
+#define USE_SERIAL_3_STDIN 0
+#define SERIAL_3_PB10_PB11 0
+#define SERIAL_3_PC10_PC11 1
+#define SERIAL_3_PD8_PD9 0
+
+#define ao_gps_getchar ao_serial2_getchar
+#define ao_gps_putchar ao_serial2_putchar
+#define ao_gps_set_speed ao_serial2_set_speed
+#define ao_gps_fifo (ao_stm_usart2.rx_fifo)
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 1
+#define HAS_USB 1
+#define HAS_BEEP 0
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 1
+#define HAS_APRS 1
+#define HAS_RADIO_RECV 0
+
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1 /* SD card */
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_PE13_PE14_PE15 0
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+#define SPI_1_PORT (&stm_gpioa)
+#define SPI_1_SCK_PIN 5
+#define SPI_1_MISO_PIN 6
+#define SPI_1_MOSI_PIN 7
+
+#define HAS_SPI_2 1
+#define SPI_2_PB13_PB14_PB15 1 /* CC115L */
+#define SPI_2_PD1_PD3_PD4 0
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#define SPI_2_PORT (&stm_gpiob)
+#define SPI_2_SCK_PIN 13
+#define SPI_2_MISO_PIN 14
+#define SPI_2_MOSI_PIN 15
+
+#define HAS_I2C_1 0
+#define I2C_1_PB8_PB9 1
+
+#define HAS_I2C_2 0
+#define I2C_2_PB10_PB11 1
+
+#define PACKET_HAS_SLAVE 0
+#define PACKET_HAS_MASTER 0
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_0_ENABLE STM_RCC_AHBENR_GPIOAEN
+#define LED_PORT_0 (&stm_gpioa)
+#define LED_PORT_0_MASK (0xff)
+#define LED_PORT_0_SHIFT 0
+#define LED_PIN_RED 0
+#define LED_PIN_GREEN 2
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS 1
+#define HAS_FLIGHT 0
+#define HAS_ADC 0
+#define HAS_LOG 0
+
+/*
+ * Telemetry monitoring
+ */
+#define HAS_MONITOR 0
+#define LEGACY_MONITOR 0
+#define HAS_MONITOR_PUT 0
+#define AO_MONITOR_LED AO_LED_GREEN
+
+/*
+ * Radio (cc115l)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 0x10b6a5
+
+#define HAS_RADIO_POWER 1
+
+#define AO_FEC_DEBUG 0
+#define AO_CC115L_SPI_CS_PORT (&stm_gpiob)
+#define AO_CC115L_SPI_CS_PIN 12
+#define AO_CC115L_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+#define AO_CC115L_SPI stm_spi2
+
+#define AO_CC115L_FIFO_INT_GPIO_IOCFG CC115L_IOCFG2
+#define AO_CC115L_FIFO_INT_PORT (&stm_gpioa)
+#define AO_CC115L_FIFO_INT_PIN (9)
+
+#define AO_CC115L_DONE_INT_GPIO_IOCFG CC115L_IOCFG0
+#define AO_CC115L_DONE_INT_PORT (&stm_gpioa)
+#define AO_CC115L_DONE_INT_PIN (10)
+
+#define HAS_RADIO_AMP 1
+
+/*
+ * Power amplifier (RFPA0133)
+ */
+
+#define AO_PA_POWER_GPIO (&stm_gpiob)
+#define AO_PA_POWER_PIN 1
+#define AO_PA_GAIN_8_GPIO (&stm_gpiob)
+#define AO_PA_GAIN_8_PIN 10
+#define AO_PA_GAIN_16_GPIO (&stm_gpiob)
+#define AO_PA_GAIN_16_PIN 11
+
+/*
+ * SD card
+ */
+
+#define AO_SDCARD_SPI_BUS AO_SPI_1_PA5_PA6_PA7
+#define AO_SDCARD_SPI_PORT SPI_1_PORT
+#define AO_SDCARD_SPI_SCK_PIN SPI_1_SCK_PIN
+#define AO_SDCARD_SPI_MISO_PIN SPI_1_MISO_PIN
+#define AO_SDCARD_SPI_MOSI_PIN SPI_1_MOSI_PIN
+#define AO_SDCARD_SPI_CS_PORT (&stm_gpioa)
+#define AO_SDCARD_SPI_CS_PIN 4
+#define AO_SDCARD_SPI stm_spi1
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2013 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 <ao.h>
+#include <ao_exti.h>
+#include <ao_fat.h>
+
+uint16_t ao_flight_number = 1;
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+// ao_led_init(LEDS_AVAILABLE);
+// ao_led_on(AO_LED_RED);
+ ao_timer_init();
+
+
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_storage_init();
+
+ ao_serial_init();
+
+ ao_cmd_init();
+
+ ao_usb_init();
+ ao_radio_init();
+
+ ao_fat_init();
+
+ ao_gps_init();
+ ao_gps_report_mega_init();
+
+ ao_telemetry_init();
+ ao_telemetry_set_interval(AO_SEC_TO_TICKS(1));
+ ao_rdf_set(1);
+
+#if HAS_SAMPLE_PROFILE
+ ao_sample_profile_init();
+#endif
+ ao_config_init();
+
+ ao_start_scheduler();
+ return 0;
+}
ao_radio_cmac_cmd.c
PRODUCT=TeleLCO-v0.1
-PRODUCT_DEF=-DMEGAMETRUM
+PRODUCT_DEF=-DTELEMEGA
IDPRODUCT=0x0023
CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g
--- /dev/null
+ao_product.h
+telemega-*.elf
--- /dev/null
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+ ao.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_companion.h \
+ ao_data.h \
+ ao_sample.h \
+ ao_pins.h \
+ altitude-pa.h \
+ ao_kalman.h \
+ ao_product.h \
+ ao_ms5607.h \
+ ao_hmc5883.h \
+ ao_mpu6000.h \
+ ao_mma655x.h \
+ ao_cc1120_CC1120.h \
+ ao_profile.h \
+ ao_task.h \
+ ao_whiten.h \
+ ao_sample_profile.h \
+ ao_mpu.h \
+ stm32l.h \
+ Makefile
+
+#
+# Common AltOS sources
+#
+# ao_hmc5883.c
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#SAMPLE_PROFILE=ao_sample_profile.c \
+# ao_sample_profile_timer.c
+#SAMPLE_PROFILE_DEF=-DHAS_SAMPLE_PROFILE=1
+
+#STACK_GUARD=ao_mpu_stm.c
+#STACK_GUARD_DEF=-DHAS_STACK_GUARD=1
+
+ALTOS_SRC = \
+ ao_interrupt.c \
+ ao_product.c \
+ ao_romconfig.c \
+ ao_cmd.c \
+ ao_config.c \
+ ao_task.c \
+ ao_led.c \
+ ao_stdio.c \
+ ao_panic.c \
+ ao_timer.c \
+ ao_mutex.c \
+ ao_serial_stm.c \
+ ao_gps_skytraq.c \
+ ao_gps_report_mega.c \
+ ao_ignite.c \
+ ao_freq.c \
+ ao_dma_stm.c \
+ ao_spi_stm.c \
+ ao_cc1120.c \
+ ao_fec_tx.c \
+ ao_fec_rx.c \
+ ao_data.c \
+ ao_ms5607.c \
+ ao_mma655x.c \
+ ao_hmc5883.c \
+ ao_adc_stm.c \
+ ao_beep_stm.c \
+ ao_storage.c \
+ ao_m25.c \
+ ao_usb_stm.c \
+ ao_exti_stm.c \
+ ao_report.c \
+ ao_i2c_stm.c \
+ ao_mpu6000.c \
+ ao_convert_pa.c \
+ ao_log.c \
+ ao_log_mega.c \
+ ao_sample.c \
+ ao_kalman.c \
+ ao_flight.c \
+ ao_telemetry.c \
+ ao_packet_slave.c \
+ ao_packet.c \
+ ao_companion.c \
+ ao_pyro.c \
+ ao_aprs.c \
+ $(PROFILE) \
+ $(SAMPLE_PROFILE) \
+ $(STACK_GUARD)
+
+PRODUCT=TeleMega-v0.1
+PRODUCT_DEF=-DTELEMEGA
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) $(SAMPLE_PROFILE_DEF) $(STACK_GUARD_DEF) -Os -g
+
+PROGNAME=telemega-v0.1
+PROG=$(PROGNAME)-$(VERSION).elf
+
+SRC=$(ALTOS_SRC) ao_telemega.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG)
+
+$(PROG): Makefile $(OBJ) altos.ld
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
+
+../altitude-pa.h: make-altitude-pa
+ nickle $< > $@
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+ $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean: clean
+
+clean:
+ rm -f *.o $(PROGNAME)-*.elf
+ rm -f ao_product.h
+
+install:
+
+uninstall:
--- /dev/null
+/*
+ * Copyright © 2012 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_TASK_QUEUE 1
+
+/* 8MHz High speed external crystal */
+#define AO_HSE 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV 3
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER 2
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER 2
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1 1
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PB6_PB7 0
+#define SERIAL_1_PA9_PA10 1
+
+#define HAS_SERIAL_2 0
+#define USE_SERIAL_2_STDIN 0
+#define SERIAL_2_PA2_PA3 0
+#define SERIAL_2_PD5_PD6 0
+
+#define HAS_SERIAL_3 1
+#define USE_SERIAL_3_STDIN 0
+#define SERIAL_3_PB10_PB11 0
+#define SERIAL_3_PC10_PC11 1
+#define SERIAL_3_PD8_PD9 0
+
+#define ao_gps_getchar ao_serial3_getchar
+#define ao_gps_putchar ao_serial3_putchar
+#define ao_gps_set_speed ao_serial3_set_speed
+#define ao_gps_fifo (ao_stm_usart3.rx_fifo)
+
+#define HAS_EEPROM 1
+#define USE_INTERNAL_FLASH 0
+#define HAS_USB 1
+#define HAS_BEEP 1
+#define HAS_RADIO 1
+#define HAS_TELEMETRY 1
+#define HAS_APRS 1
+
+#define HAS_SPI_1 1
+#define SPI_1_PA5_PA6_PA7 1 /* Barometer */
+#define SPI_1_PB3_PB4_PB5 0
+#define SPI_1_PE13_PE14_PE15 1 /* Accelerometer */
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_SPI_2 1
+#define SPI_2_PB13_PB14_PB15 1 /* Flash, Companion */
+#define SPI_2_PD1_PD3_PD4 0
+#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz
+
+#define SPI_2_PORT (&stm_gpiob)
+#define SPI_2_SCK_PIN 13
+#define SPI_2_MISO_PIN 14
+#define SPI_2_MOSI_PIN 15
+
+#define HAS_I2C_1 1
+#define I2C_1_PB8_PB9 1
+
+#define HAS_I2C_2 1
+#define I2C_2_PB10_PB11 1
+
+#define PACKET_HAS_SLAVE 1
+#define PACKET_HAS_MASTER 0
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT (&stm_gpioc)
+#define LED_PIN_RED 8
+#define LED_PIN_GREEN 9
+#define AO_LED_RED (1 << LED_PIN_RED)
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS 1
+#define HAS_FLIGHT 1
+#define HAS_ADC 1
+#define HAS_LOG 1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE 1
+#define HAS_IGNITE_REPORT 1
+
+#define AO_SENSE_DROGUE(p) ((p)->adc.sense[0])
+#define AO_SENSE_MAIN(p) ((p)->adc.sense[1])
+#define AO_IGNITER_CLOSED 400
+#define AO_IGNITER_OPEN 60
+
+#define AO_IGNITER_DROGUE_PORT (&stm_gpiod)
+#define AO_IGNITER_DROGUE_PIN 6
+
+#define AO_IGNITER_MAIN_PORT (&stm_gpiod)
+#define AO_IGNITER_MAIN_PIN 7
+
+#define AO_PYRO_PORT_0 (&stm_gpiob)
+#define AO_PYRO_PIN_0 5
+
+#define AO_PYRO_PORT_1 (&stm_gpioe)
+#define AO_PYRO_PIN_1 4
+
+#define AO_PYRO_PORT_2 (&stm_gpioe)
+#define AO_PYRO_PIN_2 6
+
+#define AO_PYRO_PORT_3 (&stm_gpioe)
+#define AO_PYRO_PIN_3 5
+
+/* Number of general purpose pyro channels available */
+#define AO_PYRO_NUM 4
+
+#define AO_IGNITER_SET_DROGUE(v) stm_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, v)
+#define AO_IGNITER_SET_MAIN(v) stm_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, v)
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING 32
+#define AO_ADC_NUM_SENSE 6
+
+struct ao_adc {
+ int16_t sense[AO_ADC_NUM_SENSE];
+ int16_t v_batt;
+ int16_t v_pbatt;
+ int16_t accel_ref;
+ int16_t accel;
+ int16_t temp;
+};
+
+#define AO_ADC_SENSE_A 0
+#define AO_ADC_SENSE_A_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_A_PIN 0
+
+#define AO_ADC_SENSE_B 1
+#define AO_ADC_SENSE_B_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_B_PIN 1
+
+#define AO_ADC_SENSE_C 2
+#define AO_ADC_SENSE_C_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_C_PIN 2
+
+#define AO_ADC_SENSE_D 3
+#define AO_ADC_SENSE_D_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_D_PIN 3
+
+#define AO_ADC_SENSE_E 4
+#define AO_ADC_SENSE_E_PORT (&stm_gpioa)
+#define AO_ADC_SENSE_E_PIN 4
+
+#define AO_ADC_SENSE_F 22
+#define AO_ADC_SENSE_F_PORT (&stm_gpioe)
+#define AO_ADC_SENSE_F_PIN 7
+
+#define AO_ADC_V_BATT 8
+#define AO_ADC_V_BATT_PORT (&stm_gpiob)
+#define AO_ADC_V_BATT_PIN 0
+
+#define AO_ADC_V_PBATT 9
+#define AO_ADC_V_PBATT_PORT (&stm_gpiob)
+#define AO_ADC_V_PBATT_PIN 1
+
+#define AO_ADC_ACCEL_REF 10
+#define AO_ADC_ACCEL_REF_PORT (&stm_gpioc)
+#define AO_ADC_ACCEL_REF_PIN 0
+
+#define AO_ADC_ACCEL 11
+#define AO_ADC_ACCEL_PORT (&stm_gpioc)
+#define AO_ADC_ACCEL_PIN 1
+
+#define AO_ADC_TEMP 16
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN) | \
+ (1 << STM_RCC_AHBENR_GPIOEEN) | \
+ (1 << STM_RCC_AHBENR_GPIOBEN) | \
+ (1 << STM_RCC_AHBENR_GPIOCEN))
+
+#define AO_NUM_ADC_PIN (AO_ADC_NUM_SENSE + 4)
+
+#define AO_ADC_PIN0_PORT AO_ADC_SENSE_A_PORT
+#define AO_ADC_PIN0_PIN AO_ADC_SENSE_A_PIN
+#define AO_ADC_PIN1_PORT AO_ADC_SENSE_B_PORT
+#define AO_ADC_PIN1_PIN AO_ADC_SENSE_B_PIN
+#define AO_ADC_PIN2_PORT AO_ADC_SENSE_C_PORT
+#define AO_ADC_PIN2_PIN AO_ADC_SENSE_C_PIN
+#define AO_ADC_PIN3_PORT AO_ADC_SENSE_D_PORT
+#define AO_ADC_PIN3_PIN AO_ADC_SENSE_D_PIN
+#define AO_ADC_PIN4_PORT AO_ADC_SENSE_E_PORT
+#define AO_ADC_PIN4_PIN AO_ADC_SENSE_E_PIN
+#define AO_ADC_PIN5_PORT AO_ADC_SENSE_F_PORT
+#define AO_ADC_PIN5_PIN AO_ADC_SENSE_F_PIN
+#define AO_ADC_PIN6_PORT AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN6_PIN AO_ADC_V_BATT_PIN
+#define AO_ADC_PIN7_PORT AO_ADC_V_PBATT_PORT
+#define AO_ADC_PIN7_PIN AO_ADC_V_PBATT_PIN
+#define AO_ADC_PIN8_PORT AO_ADC_ACCEL_REF_PORT
+#define AO_ADC_PIN8_PIN AO_ADC_ACCEL_REF_PIN
+#define AO_ADC_PIN9_PORT AO_ADC_ACCEL_PORT
+#define AO_ADC_PIN9_PIN AO_ADC_ACCEL_PIN
+
+#define AO_NUM_ADC (AO_ADC_NUM_SENSE + 5)
+
+#define AO_ADC_SQ1 AO_ADC_SENSE_A
+#define AO_ADC_SQ2 AO_ADC_SENSE_B
+#define AO_ADC_SQ3 AO_ADC_SENSE_C
+#define AO_ADC_SQ4 AO_ADC_SENSE_D
+#define AO_ADC_SQ5 AO_ADC_SENSE_E
+#define AO_ADC_SQ6 AO_ADC_SENSE_F
+#define AO_ADC_SQ7 AO_ADC_V_BATT
+#define AO_ADC_SQ8 AO_ADC_V_PBATT
+#define AO_ADC_SQ9 AO_ADC_ACCEL_REF
+#define AO_ADC_SQ10 AO_ADC_ACCEL
+#define AO_ADC_SQ11 AO_ADC_TEMP
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607 1
+#define HAS_MS5611 0
+#define AO_MS5607_PRIVATE_PINS 1
+#define AO_MS5607_CS_PORT (&stm_gpioc)
+#define AO_MS5607_CS_PIN 4
+#define AO_MS5607_CS_MASK (1 << AO_MS5607_CS)
+#define AO_MS5607_MISO_PORT (&stm_gpioa)
+#define AO_MS5607_MISO_PIN 6
+#define AO_MS5607_MISO_MASK (1 << AO_MS5607_MISO)
+#define AO_MS5607_SPI_INDEX AO_SPI_1_PA5_PA6_PA7
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS 1
+#define AO_M25_SPI_CS_PORT (&stm_gpiod)
+#define AO_M25_SPI_CS_MASK (1 << 3)
+#define AO_M25_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Radio (cc1120)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT 0x6ca333
+
+#define AO_FEC_DEBUG 0
+#define AO_CC1120_SPI_CS_PORT (&stm_gpioc)
+#define AO_CC1120_SPI_CS_PIN 5
+#define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+#define AO_CC1120_SPI stm_spi2
+
+#define AO_CC1120_INT_PORT (&stm_gpioc)
+#define AO_CC1120_INT_PIN 14
+#define AO_CC1120_MCU_WAKEUP_PORT (&stm_gpioc)
+#define AO_CC1120_MCU_WAKEUP_PIN (0)
+
+#define AO_CC1120_INT_GPIO 2
+#define AO_CC1120_INT_GPIO_IOCFG CC1120_IOCFG2
+
+#define AO_CC1120_MARC_GPIO 3
+#define AO_CC1120_MARC_GPIO_IOCFG CC1120_IOCFG3
+
+
+#define HAS_BOOT_RADIO 0
+
+/*
+ * Mag sensor (hmc5883)
+ */
+
+#define HAS_HMC5883 0
+#define AO_HMC5883_INT_PORT (&stm_gpioc)
+#define AO_HMC5883_INT_PIN 12
+#define AO_HMC5883_I2C_INDEX STM_I2C_INDEX(1)
+
+/*
+ * mpu6000
+ */
+
+#define HAS_MPU6000 1
+#define AO_MPU6000_INT_PORT (&stm_gpioc)
+#define AO_MPU6000_INT_PIN 13
+#define AO_MPU6000_I2C_INDEX STM_I2C_INDEX(1)
+
+#define HAS_HIGHG_ACCEL 0
+
+/*
+ * mma655x
+ */
+
+#define HAS_MMA655X 1
+#define AO_MMA655X_SPI_INDEX AO_SPI_1_PE13_PE14_PE15
+#define AO_MMA655X_CS_PORT (&stm_gpiod)
+#define AO_MMA655X_CS_PIN 4
+
+#define NUM_CMDS 16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT (&stm_gpiod)
+#define AO_COMPANION_CS_PIN (0)
+#define AO_COMPANION_SPI_BUS AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR 0
+#define LEGACY_MONITOR 0
+#define HAS_MONITOR_PUT 1
+#define AO_MONITOR_LED 0
+#define HAS_RSSI 0
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE 0
+#endif
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2011 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 <ao.h>
+#include <ao_hmc5883.h>
+#include <ao_mpu6000.h>
+#include <ao_mma655x.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#if HAS_SAMPLE_PROFILE
+#include <ao_sample_profile.h>
+#endif
+#include <ao_pyro.h>
+#if HAS_STACK_GUARD
+#include <ao_mpu.h>
+#endif
+
+int
+main(void)
+{
+ ao_clock_init();
+
+#if HAS_STACK_GUARD
+ ao_mpu_init();
+#endif
+
+ ao_task_init();
+ ao_serial_init();
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_GREEN);
+ ao_timer_init();
+
+ ao_i2c_init();
+ ao_spi_init();
+ ao_dma_init();
+ ao_exti_init();
+
+ ao_adc_init();
+#if HAS_BEEP
+ ao_beep_init();
+#endif
+ ao_cmd_init();
+
+#if HAS_MS5607
+ ao_ms5607_init();
+#endif
+#if HAS_HMC5883
+ ao_hmc5883_init();
+#endif
+#if HAS_MPU6000
+ ao_mpu6000_init();
+#endif
+#if HAS_MMA655X
+ ao_mma655x_init();
+#endif
+
+ ao_storage_init();
+
+ ao_flight_init();
+ ao_log_init();
+ ao_report_init();
+
+ ao_usb_init();
+ ao_gps_init();
+ ao_gps_report_mega_init();
+ ao_telemetry_init();
+ ao_radio_init();
+ ao_packet_slave_init(FALSE);
+ ao_igniter_init();
+ ao_companion_init();
+ ao_pyro_init();
+
+ ao_config_init();
+#if AO_PROFILE
+ ao_profile_init();
+#endif
+#if HAS_SAMPLE_PROFILE
+ ao_sample_profile_init();
+#endif
+
+ ao_start_scheduler();
+ return 0;
+}
--- /dev/null
+ST discovery card pins
+
+1 AIN-1
+2 JTCK
+3 GND
+4 JTMS
+5 NRST
+6 SWO
+
+MegaMetrum v0.1 misc connector
+
+1 GND
+2 reset_n
+3 boot0
+4 tx1
+5 rx1
+6 +3.3V
+7 GND
+8 jtms
+9 jtck
+10 jtdi
+11 jtdo
+12 jntrst
+13 sda2
+14 scl2
+15 pe1
+16 pe0
+
+For debugging:
+
+ ST MM v0.1
+JTCK 2 9
+GND 3 7
+JTMS 4 8
+NRST 5 2
+
+Altus Metrum STM32L standard debug connector (4 pin MicoMaTch):
+
+ TL ST
+GND 1 3
+NRST 2 5
+SWDIO 3 4
+SWCLK 4 2
+
+Altus Metrum standard 4-pin connector to MegaMetrum v0.1 misc connector:
+
+ AMstd MM v0.1
+gnd 1 1
+nrst 2 2
+swdio 3 8
+swclk 4 9
+
+MegaAccel:
+
+Jumpers
+PC0 (pin15) (blue) PE0 (pin97) accel_ref (debug 16)
+PC1 (pin16) (green) PE1 (pin98) accel (debug 15)
#define AO_LED_RED 1
#define AO_LED_GREEN 2
#define AO_MONITOR_LED AO_LED_RED
+ #define AO_BT_LED AO_LED_GREEN
#define LEDS_AVAILABLE (AO_LED_RED|AO_LED_GREEN)
#define SPI_CS_ON_P1 1
#define SPI_CS_ON_P0 0
#define LEGACY_MONITOR 0
#define HAS_RSSI 0
#define HAS_AES 0
+ #define HAS_RADIO 1
#endif
#if DBG_ON_P1
ao_gps_test_skytraq
ao_convert_test
ao_convert_pa_test
+ao_fat_test
ao_fec_test
ao_flight_test_mm
ao_flight_test_noisy_accel
PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_flight_test_mm \
ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test \
- ao_aprs_test ao_micropeak_test
+ ao_aprs_test ao_micropeak_test ao_fat_test
INCS=ao_kalman.h ao_ms5607.h ao_log.h ao_data.h altitude-pa.h altitude.h
cc $(CFLAGS) -o $@ -DFORCE_ACCEL=1 ao_flight_test.c
ao_flight_test_mm: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c $(INCS)
- cc -DMEGAMETRUM=1 $(CFLAGS) -o $@ $< -lm
+ cc -DTELEMEGA=1 $(CFLAGS) -o $@ $< -lm
ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h
cc $(CFLAGS) -o $@ $<
ao_micropeak_test: ao_micropeak_test.c ao_microflight.c ao_kalman.h
cc $(CFLAGS) -o $@ ao_micropeak_test.c -lm
+
+ao_fat_test: ao_fat_test.c ao_fat.c ao_bufio.c
+ cc $(CFLAGS) -o $@ ao_fat_test.c -lssl
}
void
-ao_radio_send_lots(ao_radio_fill_func fill)
+ao_radio_send_aprs(ao_radio_fill_func fill)
{
int16_t len;
uint8_t done = 0;
--- /dev/null
+/*
+ * Copyright © 2013 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <getopt.h>
+#include <math.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <openssl/md5.h>
+
+#define AO_FAT_TEST
+
+void
+ao_mutex_get(uint8_t *mutex)
+{
+}
+
+void
+ao_mutex_put(uint8_t *mutex)
+{
+}
+
+void
+ao_panic(uint8_t panic)
+{
+ printf ("panic %d\n", panic);
+ abort();
+}
+
+#define AO_PANIC_BUFIO 15
+
+#define ao_cmd_success 0
+
+uint8_t ao_cmd_status;
+uint32_t ao_cmd_lex_u32;
+
+void
+ao_cmd_decimal()
+{
+}
+
+#define ao_cmd_register(x)
+
+struct ao_cmds {
+ void (*func)(void);
+ const char *help;
+};
+
+int fs_fd;
+
+uint64_t total_reads, total_writes;
+
+uint8_t
+ao_sdcard_read_block(uint32_t block, uint8_t *data)
+{
+ ++total_reads;
+ lseek(fs_fd, block * 512, 0);
+ return read(fs_fd, data, 512) == 512;
+}
+
+uint8_t
+ao_sdcard_write_block(uint32_t block, uint8_t *data)
+{
+ ++total_writes;
+ lseek(fs_fd, block * 512, 0);
+ return write(fs_fd, data, 512) == 512;
+}
+
+struct fs_param {
+ int fat;
+ int blocks;
+} fs_params[] = {
+ { .fat = 16, .blocks = 16384 },
+ { .fat = 32, .blocks = 16384 },
+ { .fat = 16, .blocks = 65536 },
+ { .fat = 32, .blocks = 65536 },
+ { .fat = 16, .blocks = 1048576 },
+ { .fat = 32, .blocks = 1048576 },
+ { .fat = 0, .blocks = 0 },
+};
+
+char *fs = "fs.fat";
+struct fs_param *param;
+
+void
+ao_sdcard_init(void)
+{
+ char cmd[1024];
+
+ if (fs_fd) {
+ close(fs_fd);
+ fs_fd = 0;
+ }
+ snprintf(cmd, sizeof(cmd), "rm -f %s && mkfs.vfat -F %d -C %s %d",
+ fs, param->fat, fs, param->blocks);
+ if (system (cmd) != 0) {
+ fprintf(stderr, "'%s' failed\n", cmd);
+ exit(1);
+ }
+ fs_fd = open(fs, 2);
+ if (fs_fd < 0) {
+ perror (fs);
+ exit(1);
+ }
+}
+
+#include "ao_bufio.c"
+void
+check_bufio(char *where)
+{
+ int b;
+
+ for (b = 0; b < AO_NUM_BUF; b++) {
+ if (ao_bufio[b].busy) {
+ printf ("%s: buffer %d busy. block %d seqno %u\n",
+ where, b, ao_bufio[b].block, ao_bufio[b].seqno & 0xffff);
+ abort();
+ }
+ }
+}
+
+
+void
+check_fat(void);
+
+#include "ao_fat.c"
+
+/* Get the next cluster entry in the chain */
+static cluster_t
+ao_fat_entry_raw_read(cluster_t cluster, uint8_t fat)
+{
+ sector_t sector;
+ cluster_offset_t offset;
+ uint8_t *buf;
+ cluster_t ret;
+
+ if (fat32)
+ cluster <<= 2;
+ else
+ cluster <<= 1;
+ sector = cluster >> SECTOR_SHIFT;
+ offset = cluster & SECTOR_MASK;
+ buf = _ao_fat_sector_get(fat_start + fat * sectors_per_fat + sector);
+ if (!buf)
+ return 0;
+ if (fat32)
+ ret = get_u32(buf + offset);
+ else
+ ret = get_u16(buf + offset);
+ _ao_fat_sector_put(buf, 0);
+ return ret;
+}
+
+void
+dump_fat(void)
+{
+ int e;
+
+ printf ("\n **** FAT ****\n\n");
+ for (e = 0; e < number_cluster; e++) {
+ if ((e & 0xf) == 0x0)
+ printf ("%04x: ", e);
+ if (fat32)
+ printf (" %08x", ao_fat_entry_raw_read(e, 0));
+ else
+ printf (" %04x", ao_fat_entry_raw_read(e, 0));
+ if ((e & 0xf) == 0xf)
+ putchar ('\n');
+ }
+ if (e & 0xf)
+ putchar('\n');
+}
+
+void
+fat_list(void)
+{
+ dirent_t entry = 0;
+ struct ao_fat_dirent dirent;
+
+ printf (" **** Root directory ****\n");
+ while (ao_fat_readdir(&entry, &dirent)) {
+ printf ("%04x: %-8.8s.%-3.3s %02x %04x %d\n",
+ entry,
+ dirent.name,
+ dirent.name + 8,
+ dirent.attr,
+ dirent.cluster,
+ dirent.size);
+ }
+
+ printf (" **** End of root directory ****\n");
+}
+
+void
+fatal(char *msg, ...)
+{
+// dump_fat();
+// fat_list();
+
+ va_list l;
+ va_start(l, msg);
+ vfprintf(stderr, msg, l);
+ va_end(l);
+
+ abort();
+}
+
+void
+check_fat(void)
+{
+ cluster_t e;
+ int f;
+
+ for (e = 0; e < number_cluster; e++) {
+ cluster_t v = ao_fat_entry_raw_read(e, 0);
+ for (f = 1; f < number_fat; f++) {
+ cluster_t o = ao_fat_entry_raw_read(e, f);
+ if (o != v)
+ fatal ("fats differ at %08x (0 %08x %d %08x)\n", e, v, f, o);
+ }
+ }
+}
+
+cluster_t
+check_file(dirent_t dent, cluster_t first_cluster, dirent_t *used)
+{
+ cluster_t clusters = 0;
+ cluster_t cluster;
+
+ if (!first_cluster)
+ return 0;
+
+ for (cluster = first_cluster;
+ fat32 ? !AO_FAT_IS_LAST_CLUSTER(cluster) : !AO_FAT_IS_LAST_CLUSTER16(cluster);
+ cluster = ao_fat_entry_raw_read(cluster, 0))
+ {
+ if (!_ao_fat_cluster_valid(cluster))
+ fatal("file %d: invalid cluster %08x\n", dent, cluster);
+ if (used[cluster])
+ fatal("file %d: duplicate cluster %08x also in file %d\n", dent, cluster, used[cluster]-1);
+ used[cluster] = dent;
+ clusters++;
+ }
+ return clusters;
+}
+
+void
+check_fs(void)
+{
+ dirent_t r;
+ cluster_t cluster, chain;
+ dirent_t *used;
+ uint8_t *dent;
+
+ check_fat();
+
+ used = calloc(sizeof (dirent_t), number_cluster);
+
+ for (r = 0; (dent = _ao_fat_root_get(r)); r++) {
+ cluster_t clusters;
+ offset_t size;
+ cluster_t first_cluster;
+ char name[11];
+
+ if (!dent)
+ fatal("cannot map dent %d\n", r);
+ memcpy(name, dent+0, 11);
+ first_cluster = get_u16(dent + 0x1a);
+ if (fat32)
+ first_cluster |= (cluster_t) get_u16(dent + 0x14) << 16;
+ size = get_u32(dent + 0x1c);
+ _ao_fat_root_put(dent, r, 0);
+
+ if (name[0] == AO_FAT_DENT_END) {
+ break;
+ }
+
+ clusters = check_file(r + 1, first_cluster, used);
+ if (size == 0) {
+ if (clusters != 0)
+ fatal("file %d: zero sized, but %d clusters\n", clusters);
+ } else {
+ if (size > clusters * bytes_per_cluster)
+ fatal("file %d: size %u beyond clusters %d (%u)\n",
+ r, size, clusters, clusters * bytes_per_cluster);
+ if (size <= (clusters - 1) * bytes_per_cluster)
+ fatal("file %d: size %u too small clusters %d (%u)\n",
+ r, size, clusters, clusters * bytes_per_cluster);
+ }
+ }
+ if (!fat32) {
+ for (; r < root_entries; r++) {
+ uint8_t *dent = _ao_fat_root_get(r);
+ if (!dent)
+ fatal("cannot map dent %d\n", r);
+ if (dent[0] != AO_FAT_DENT_END)
+ fatal("found non-zero dent past end %d\n", r);
+ _ao_fat_root_put(dent, r, 0);
+ }
+ } else {
+ check_file((dirent_t) -1, root_cluster, used);
+ }
+
+ for (cluster = 0; cluster < 2; cluster++) {
+ chain = ao_fat_entry_raw_read(cluster, 0);
+
+ if (fat32) {
+ if ((chain & 0xffffff8) != 0xffffff8)
+ fatal("cluster %d: not marked busy\n", cluster);
+ } else {
+ if ((chain & 0xfff8) != 0xfff8)
+ fatal("cluster %d: not marked busy\n", cluster);
+ }
+ }
+ for (; cluster < number_cluster; cluster++) {
+ chain = ao_fat_entry_raw_read(cluster, 0);
+
+ if (chain != 0) {
+ if (used[cluster] == 0)
+ fatal("cluster %d: marked busy, but not in any file\n", cluster);
+ } else {
+ if (used[cluster] != 0)
+ fatal("cluster %d: marked free, but found in file %d\n", cluster, used[cluster]-1);
+ }
+ }
+}
+
+#define NUM_FILES 100
+#define LINES_FILE 500000
+
+uint32_t sizes[NUM_FILES];
+
+unsigned char md5[NUM_FILES][MD5_DIGEST_LENGTH];
+
+void
+micro_test_fs(void)
+{
+ int8_t fd;
+ char name[] = "FOO ";
+ char buf[512];
+ int len;
+
+ printf ("write once\n");
+ if ((fd = ao_fat_creat(name)) >= 0) {
+ ao_fat_write(fd, "hello world\n", 12);
+ ao_fat_close(fd);
+ }
+
+ printf ("read first\n");
+ if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+ len = ao_fat_read(fd, buf, sizeof(buf));
+ write (1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ printf ("write again\n");
+ if ((fd = ao_fat_creat(name)) >= 0) {
+ ao_fat_write(fd, "hi\n", 3);
+ ao_fat_close(fd);
+ }
+
+ printf ("read again\n");
+ if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+ len = ao_fat_read(fd, buf, sizeof(buf));
+ write (1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ printf ("write 3\n");
+ if ((fd = ao_fat_creat(name)) >= 0) {
+ int l;
+ char c;
+
+ for (l = 0; l < 10; l++) {
+ for (c = ' '; c < '~'; c++)
+ ao_fat_write(fd, &c, 1);
+ c = '\n';
+ ao_fat_write(fd, &c, 1);
+ }
+ ao_fat_close(fd);
+ }
+
+ printf ("read 3\n");
+ if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+ while ((len = ao_fat_read(fd, buf, sizeof(buf))) > 0)
+ write (1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ check_fs();
+ printf ("all done\n");
+}
+
+
+void
+short_test_fs(void)
+{
+ int len;
+ int8_t fd;
+ char buf[345];
+
+ if ((fd = ao_fat_open("HELLO TXT",AO_FAT_OPEN_READ)) >= 0) {
+ printf ("File contents for HELLO.TXT\n");
+ while ((len = ao_fat_read(fd, buf, sizeof(buf))))
+ write(1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ if ((fd = ao_fat_creat("NEWFILE TXT")) >= 0) {
+ printf ("Create new file\n");
+ for (len = 0; len < 2; len++)
+ ao_fat_write(fd, "hello, world!\n", 14);
+ ao_fat_seek(fd, 0, AO_FAT_SEEK_SET);
+ printf ("read new file\n");
+ while ((len = ao_fat_read(fd, buf, sizeof (buf))))
+ write (1, buf, len);
+ ao_fat_close(fd);
+ }
+
+ check_fs();
+}
+
+void
+long_test_fs(void)
+{
+ char name[12];
+ int id;
+ MD5_CTX ctx;
+ unsigned char md5_check[MD5_DIGEST_LENGTH];
+ char buf[337];
+ int len;
+ int8_t fd;
+ uint64_t total_file_size = 0;
+
+ total_reads = total_writes = 0;
+
+ printf (" **** Creating %d files\n", NUM_FILES);
+
+ memset(sizes, '\0', sizeof (sizes));
+ for (id = 0; id < NUM_FILES; id++) {
+ sprintf(name, "D%07dTXT", id);
+ if ((id % ((NUM_FILES+49)/50)) == 0) {
+ printf ("."); fflush(stdout);
+ }
+ if ((fd = ao_fat_creat(name)) >= 0) {
+ int j;
+ char line[64];
+ check_bufio("file created");
+ MD5_Init(&ctx);
+ for (j = 0; j < LINES_FILE; j++) {
+ int len, ret;
+ sprintf (line, "Hello, world %d %d\r\n", id, j);
+ len = strlen(line);
+ ret = ao_fat_write(fd, line, len);
+ if (ret <= 0)
+ break;
+ total_file_size += ret;
+ MD5_Update(&ctx, line, ret);
+ sizes[id] += ret;
+ if (ret != len)
+ printf ("write failed %d\n", ret);
+ }
+ ao_fat_close(fd);
+ MD5_Final(&md5[id][0], &ctx);
+ check_bufio("file written");
+ }
+ }
+
+ printf ("\n **** Write IO: read %llu write %llu data sectors %llu\n", total_reads, total_writes, (total_file_size + 511) / 512);
+
+ check_bufio("all files created");
+ printf (" **** All done creating files\n");
+ check_fs();
+
+ total_reads = total_writes = 0;
+
+ printf (" **** Comparing %d files\n", NUM_FILES);
+
+ for (id = 0; id < NUM_FILES; id++) {
+ uint32_t size;
+ sprintf(name, "D%07dTXT", id);
+ size = 0;
+ if ((id % ((NUM_FILES+49)/50)) == 0) {
+ printf ("."); fflush(stdout);
+ }
+ if ((fd = ao_fat_open(name, AO_FAT_OPEN_READ)) >= 0) {
+ MD5_Init(&ctx);
+ while ((len = ao_fat_read(fd, buf, sizeof(buf))) > 0) {
+ MD5_Update(&ctx, buf, len);
+ size += len;
+ }
+ ao_fat_close(fd);
+ MD5_Final(md5_check, &ctx);
+ if (size != sizes[id])
+ fatal("file %d: size differs %d written %d read\n",
+ id, sizes[id], size);
+ if (memcmp(md5_check, &md5[id][0], sizeof (md5_check)) != 0)
+ fatal ("file %d: checksum failed\n", id);
+ check_bufio("file shown");
+ }
+ }
+ printf ("\n **** Read IO: read %llu write %llu\n", total_reads, total_writes);
+}
+
+char *params[] = {
+ "-F 16 -C %s 16384",
+ "-F 32 -C %s 16384",
+ "-F 16 -C %s 65536",
+ "-F 32 -C %s 65536",
+ "-F 16 -C %s 1048576",
+ "-F 32 -C %s 1048576",
+ NULL
+};
+
+void
+do_test(void (*test)(void))
+{
+ ao_fat_init();
+
+ check_bufio("top");
+ _ao_fat_setup();
+
+ check_fs();
+ check_bufio("after setup");
+ (*test)();
+ ao_fat_unmount();
+}
+
+int
+main(int argc, char **argv)
+{
+ int p;
+
+ if (argv[1])
+ fs = argv[1];
+
+ for (p = 0; fs_params[p].fat; p++) {
+ param = &fs_params[p];
+
+ do_test(micro_test_fs);
+ do_test(short_test_fs);
+ do_test(long_test_fs);
+ }
+ unlink (fs);
+
+ return 0;
+}
#define AO_MS_TO_SPEED(ms) ((int16_t) ((ms) * 16))
#define AO_MSS_TO_ACCEL(mss) ((int16_t) ((mss) * 16))
-#if MEGAMETRUM
+#if TELEMEGA
#define AO_ADC_NUM_SENSE 6
#define HAS_MS5607 1
#define HAS_MPU6000 1
#define ao_xmemcmp(d,s,c) memcmp(d,s,c)
#define AO_NEED_ALTITUDE_TO_PRES 1
-#if MEGAMETRUM
+#if TELEMEGA
#include "ao_convert_pa.c"
#include <ao_ms5607.h>
struct ao_ms5607_prom ms5607_prom;
#else
double accel = 0.0;
#endif
-#if MEGAMETRUM
+#if TELEMEGA
double height;
ao_ms5607_convert(&ao_data_static.ms5607_raw, &ao_data_static.ms5607_cooked);
if (!ao_summary) {
printf("%7.2f height %8.2f accel %8.3f "
-#if MEGAMETRUM
+#if TELEMEGA
"roll %8.3f angle %8.3f qangle %8.3f "
"accel_x %8.3f accel_y %8.3f accel_z %8.3f gyro_x %8.3f gyro_y %8.3f gyro_z %8.3f "
#endif
time,
height,
accel,
-#if MEGAMETRUM
+#if TELEMEGA
ao_mpu6000_gyro(ao_sample_roll_angle) / 100.0,
ao_mpu6000_gyro(ao_sample_angle) / 100.0,
ao_sample_qangle,
static int log_format;
-#if MEGAMETRUM
+#if TELEMEGA
static double
ao_vec_norm(double x, double y, double z)
for (;;) {
if (ao_records_read > 2 && ao_flight_state == ao_flight_startup)
{
-#if MEGAMETRUM
+#if TELEMEGA
ao_data_static.mpu6000 = ao_ground_mpu6000;
#else
ao_data_static.adc.accel = ao_flight_ground_accel;
if (words[nword] == NULL)
break;
}
-#if MEGAMETRUM
- if (log_format == AO_LOG_FORMAT_MEGAMETRUM && nword == 30 && strlen(words[0]) == 1) {
+#if TELEMEGA
+ if (log_format == AO_LOG_FORMAT_TELEMEGA && nword == 30 && strlen(words[0]) == 1) {
int i;
struct ao_ms5607_value value;
continue;
}
#else
- if (nword == 4 && log_format != AO_LOG_FORMAT_MEGAMETRUM) {
+ if (nword == 4 && log_format != AO_LOG_FORMAT_TELEMEGA) {
type = words[0][0];
tick = strtoul(words[1], NULL, 16);
a = strtoul(words[2], NULL, 16);
if (type != 'F' && !ao_flight_started)
continue;
-#if MEGAMETRUM
+#if TELEMEGA
(void) a;
(void) b;
#else
%TeleMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
%TeleDongle% = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
%TeleTerra% = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+%TeleBT% = AltusMetrum.Install, USB\VID_FFFE&PID_000e, AltusMetrumSerial\r
+%TeleLaunch% = AltusMetrum.Install, USB\VID_FFFE&PID_000f, AltusMetrumSerial\r
+%TeleLCO% = AltusMetrum.Install, USB\VID_FFFE&PID_0010, AltusMetrumSerial\r
+%TeleScience% = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial\r
+%TelePyro% = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial\r
+%TeleShield% = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial\r
+%TeleMega% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial\r
+%MegaDongle = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial\r
+%TeleGPS% = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial\r
+%AltusMetrum26% = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial\r
+%AltusMetrum27% = AltusMetrum.Install, USB\VID_FFFE&PID_0027, AltusMetrumSerial\r
+%AltusMetrum28% = AltusMetrum.Install, USB\VID_FFFE&PID_0028, AltusMetrumSerial\r
+%AltusMetrum29% = AltusMetrum.Install, USB\VID_FFFE&PID_0029, AltusMetrumSerial\r
+%AltusMetrum2a% = AltusMetrum.Install, USB\VID_FFFE&PID_002a, AltusMetrumSerial\r
+%AltusMetrum2b% = AltusMetrum.Install, USB\VID_FFFE&PID_002b, AltusMetrumSerial\r
+%AltusMetrum2c% = AltusMetrum.Install, USB\VID_FFFE&PID_002c, AltusMetrumSerial\r
\r
[Models.NTx86]\r
%AltusMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
%TeleMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
%TeleDongle% = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
%TeleTerra% = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+%TeleBT% = AltusMetrum.Install, USB\VID_FFFE&PID_000e, AltusMetrumSerial\r
+%TeleLaunch% = AltusMetrum.Install, USB\VID_FFFE&PID_000f, AltusMetrumSerial\r
+%TeleLCO% = AltusMetrum.Install, USB\VID_FFFE&PID_0010, AltusMetrumSerial\r
+%TeleScience% = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial\r
+%TelePyro% = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial\r
+%TeleShield% = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial\r
+%TeleMega% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial\r
+%MegaDongle = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial\r
+%TeleGPS% = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial\r
+%AltusMetrum26% = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial\r
+%AltusMetrum27% = AltusMetrum.Install, USB\VID_FFFE&PID_0027, AltusMetrumSerial\r
+%AltusMetrum28% = AltusMetrum.Install, USB\VID_FFFE&PID_0028, AltusMetrumSerial\r
+%AltusMetrum29% = AltusMetrum.Install, USB\VID_FFFE&PID_0029, AltusMetrumSerial\r
+%AltusMetrum2a% = AltusMetrum.Install, USB\VID_FFFE&PID_002a, AltusMetrumSerial\r
+%AltusMetrum2b% = AltusMetrum.Install, USB\VID_FFFE&PID_002b, AltusMetrumSerial\r
+%AltusMetrum2c% = AltusMetrum.Install, USB\VID_FFFE&PID_002c, AltusMetrumSerial\r
\r
[Models.NTamd64]\r
%AltusMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
%TeleMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
%TeleDongle% = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
%TeleTerra% = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+%TeleBT% = AltusMetrum.Install, USB\VID_FFFE&PID_000e, AltusMetrumSerial\r
+%TeleLaunch% = AltusMetrum.Install, USB\VID_FFFE&PID_000f, AltusMetrumSerial\r
+%TeleLCO% = AltusMetrum.Install, USB\VID_FFFE&PID_0010, AltusMetrumSerial\r
+%TeleScience% = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial\r
+%TelePyro% = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial\r
+%TeleShield% = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial\r
+%TeleMega% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial\r
+%MegaDongle = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial\r
+%TeleGPS% = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial\r
+%AltusMetrum26% = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial\r
+%AltusMetrum27% = AltusMetrum.Install, USB\VID_FFFE&PID_0027, AltusMetrumSerial\r
+%AltusMetrum28% = AltusMetrum.Install, USB\VID_FFFE&PID_0028, AltusMetrumSerial\r
+%AltusMetrum29% = AltusMetrum.Install, USB\VID_FFFE&PID_0029, AltusMetrumSerial\r
+%AltusMetrum2a% = AltusMetrum.Install, USB\VID_FFFE&PID_002a, AltusMetrumSerial\r
+%AltusMetrum2b% = AltusMetrum.Install, USB\VID_FFFE&PID_002b, AltusMetrumSerial\r
+%AltusMetrum2c% = AltusMetrum.Install, USB\VID_FFFE&PID_002c, AltusMetrumSerial\r
\r
[Models.NTia64]\r
%AltusMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
%TeleMetrum% = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
%TeleDongle% = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
%TeleTerra% = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+%TeleBT% = AltusMetrum.Install, USB\VID_FFFE&PID_000e, AltusMetrumSerial\r
+%TeleLaunch% = AltusMetrum.Install, USB\VID_FFFE&PID_000f, AltusMetrumSerial\r
+%TeleLCO% = AltusMetrum.Install, USB\VID_FFFE&PID_0010, AltusMetrumSerial\r
+%TeleScience% = AltusMetrum.Install, USB\VID_FFFE&PID_0011, AltusMetrumSerial\r
+%TelePyro% = AltusMetrum.Install, USB\VID_FFFE&PID_0012, AltusMetrumSerial\r
+%TeleShield% = AltusMetrum.Install, USB\VID_FFFE&PID_0013, AltusMetrumSerial\r
+%TeleMega% = AltusMetrum.Install, USB\VID_FFFE&PID_0023, AltusMetrumSerial\r
+%MegaDongle = AltusMetrum.Install, USB\VID_FFFE&PID_0024, AltusMetrumSerial\r
+%TeleGPS% = AltusMetrum.Install, USB\VID_FFFE&PID_0025, AltusMetrumSerial\r
+%AltusMetrum26% = AltusMetrum.Install, USB\VID_FFFE&PID_0026, AltusMetrumSerial\r
+%AltusMetrum27% = AltusMetrum.Install, USB\VID_FFFE&PID_0027, AltusMetrumSerial\r
+%AltusMetrum28% = AltusMetrum.Install, USB\VID_FFFE&PID_0028, AltusMetrumSerial\r
+%AltusMetrum29% = AltusMetrum.Install, USB\VID_FFFE&PID_0029, AltusMetrumSerial\r
+%AltusMetrum2a% = AltusMetrum.Install, USB\VID_FFFE&PID_002a, AltusMetrumSerial\r
+%AltusMetrum2b% = AltusMetrum.Install, USB\VID_FFFE&PID_002b, AltusMetrumSerial\r
+%AltusMetrum2c% = AltusMetrum.Install, USB\VID_FFFE&PID_002c, AltusMetrumSerial\r
\r
;----------------------------------------------------------------------------\r
; Installation sections\r