Skip to content

Commit

Permalink
Add an option to rotate the view 90 degrees for telescopes. (sky-map-…
Browse files Browse the repository at this point in the history
…team#452)

This change adds a new viewing mode that shows the sky as it is in front of the phone, ie as pointed to by the long edge. The purpose is for telescope users to be able to strap the phone to a telescope's tube.

Took this opportunity to also update the deprecated sensorlistener code to its modern equivalent, which involved a lot of switching around of axes etc to remove the 'fixes' we had to apply to Android's originally buggy sensor code


* Rename some constants to do with the screen rotation so they make more
sense.

* Telescope coordinates.

The view is now as if you're sighting along the edge of the phone
instead of into it.  It's almost right...just the screen seems to be
upside down.

* Update the legacy sensor code to use SensorEventListener instead of the
deprecated SensorListener. This also allows for some sanitization of the
code since we no longer have to deal with various coordinate systems
being backwards.

* Update the docs some more.

* Wired up the 'telescope mode' option.

* Fixed the tests now that the sensor readings have changed.

* Update version number as the Play Store got confused and incorrectly
marked the
previous version number as used.

* Change the name of the new mode and clean up some comments.

* Fix some text in the design doc.

* Change a label to match the casing of the others.
  • Loading branch information
jaydeetay authored Dec 30, 2021
1 parent b296f6e commit 4b46400
Show file tree
Hide file tree
Showing 19 changed files with 373 additions and 250 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ android {
applicationId "com.google.android.stardroid"
minSdkVersion 26
targetSdkVersion 31
versionCode 1532
versionName "1.10.0 - RC3"
versionCode 1541
versionName "1.10.1 - RC1"
buildConfigField 'String', 'GOOGLE_ANALYTICS_CODE', '""'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ object ApplicationConstants {
const val SENSOR_DAMPING_STANDARD = "STANDARD"
const val SENSOR_DAMPING_PREF_KEY = "sensor_damping"
const val REVERSE_MAGNETIC_Z_PREFKEY = "reverse_magnetic_z"
const val ROTATE_HORIZON_PREFKEY = "rotate_horizon" // End Preference Keys
const val VIEW_MODE_PREFKEY = "viewing_direction" // End Preference Keys
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ public DynamicStarMapComponent getComponent() {
private static final class RendererModelUpdateClosure implements Runnable {
private RendererController rendererController;
private AstronomerModel model;
private boolean horizontalRotation;
private boolean viewDirectionMode;

public RendererModelUpdateClosure(AstronomerModel model,
RendererController rendererController, SharedPreferences sharedPreferences) {
this.model = model;
this.rendererController = rendererController;
this.horizontalRotation = sharedPreferences.getBoolean(ApplicationConstants.ROTATE_HORIZON_PREFKEY, false);
model.setHorizontalRotation(this.horizontalRotation);
// TODO(jontayler): figure out why we need to do this here.
updateViewDirectionMode(model, sharedPreferences);
}

@Override
Expand All @@ -139,6 +139,21 @@ public void run() {
}
}

private static void updateViewDirectionMode(AstronomerModel model, SharedPreferences sharedPreferences) {
String viewDirectionMode =
sharedPreferences.getString(ApplicationConstants.VIEW_MODE_PREFKEY, "STANDARD");
switch(viewDirectionMode) {
case "ROTATE90":
model.setViewDirectionMode(AstronomerModel.ViewDirectionMode.ROTATE90);
break;
case "TELESCOPE":
model.setViewDirectionMode(AstronomerModel.ViewDirectionMode.TELESCOPE);
break;
default:
model.setViewDirectionMode(AstronomerModel.ViewDirectionMode.STANDARD);
}
}

// Activity for result Ids
public static final int GOOGLE_PLAY_SERVICES_REQUEST_CODE = 1;
public static final int GOOGLE_PLAY_SERVICES_REQUEST_LOCATION_PERMISSION_CODE = 2;
Expand Down Expand Up @@ -563,8 +578,8 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
}
setAutoMode(autoMode);
break;
case ApplicationConstants.ROTATE_HORIZON_PREFKEY:
model.setHorizontalRotation(sharedPreferences.getBoolean(key, false));
case ApplicationConstants.VIEW_MODE_PREFKEY:
updateViewDirectionMode(model, sharedPreferences);
default:
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ void updateLineOfSight(Vector3 newLineOfSight) {

void setFieldOfView(float degrees);

void setHorizontalRotation(boolean value);
enum ViewDirectionMode {STANDARD, ROTATE90, TELESCOPE}
void setViewDirectionMode(ViewDirectionMode value);

float getMagneticCorrection();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ import kotlin.math.abs
*/
class AstronomerModelImpl(magneticDeclinationCalculator: MagneticDeclinationCalculator) :
AstronomerModel {
private var screenInPhoneCoords = SCREEN_UP_IN_PHONE_COORDS
private var pointingInPhoneCoords = POINTING_DIR_IN_STANDARD_PHONE_COORDS
private var screenUpInPhoneCoords = SCREEN_UP_STANDARD_IN_PHONE_COORDS
private var magneticDeclinationCalculator: MagneticDeclinationCalculator? = null
private var autoUpdatePointing = true
private var fieldOfView = 45f // Degrees
Expand Down Expand Up @@ -106,12 +107,23 @@ class AstronomerModelImpl(magneticDeclinationCalculator: MagneticDeclinationCalc

/** [North, Up, East] in celestial coordinates. */
private var axesMagneticCelestialMatrix = identity
override fun setHorizontalRotation(value: Boolean) {
screenInPhoneCoords = if (value) {
SCREEN_DOWN_IN_PHONE_COORDS
} else {
SCREEN_UP_IN_PHONE_COORDS
override fun setViewDirectionMode(mode: AstronomerModel.ViewDirectionMode) {
val (p, s) = when (mode) {
AstronomerModel.ViewDirectionMode.STANDARD -> listOf(
POINTING_DIR_IN_STANDARD_PHONE_COORDS,
SCREEN_UP_STANDARD_IN_PHONE_COORDS
)
AstronomerModel.ViewDirectionMode.ROTATE90 -> listOf(
POINTING_DIR_IN_STANDARD_PHONE_COORDS,
SCREEN_UP_ROTATED_IN_PHONE_COORDS
)
AstronomerModel.ViewDirectionMode.TELESCOPE -> listOf(
POINTING_DIR_FOR_TELESCOPES,
SCREEN_UP_FOR_TELESCOPES
)
}
pointingInPhoneCoords = p
screenUpInPhoneCoords = s
}

override fun setAutoUpdatePointing(autoUpdatePointing: Boolean) {
Expand Down Expand Up @@ -224,8 +236,8 @@ class AstronomerModelImpl(magneticDeclinationCalculator: MagneticDeclinationCalc
calculateLocalNorthAndUpInCelestialCoords(false)
calculateLocalNorthAndUpInPhoneCoordsFromSensors()
val transform = axesMagneticCelestialMatrix * axesPhoneInverseMatrix
val viewInSpaceSpace = transform * POINTING_DIR_IN_PHONE_COORDS
val screenUpInSpaceSpace = transform * screenInPhoneCoords
val viewInSpaceSpace = transform * pointingInPhoneCoords
val screenUpInSpaceSpace = transform * screenUpInPhoneCoords
pointing.updateLineOfSight(viewInSpaceSpace)
pointing.updatePerpendicular(screenUpInSpaceSpace)
}
Expand Down Expand Up @@ -285,30 +297,28 @@ class AstronomerModelImpl(magneticDeclinationCalculator: MagneticDeclinationCalc
magneticEastPhone = Vector3(rotationMatrix[0], rotationMatrix[1], rotationMatrix[2])
} else {
// TODO(johntaylor): we can reduce the number of vector copies done in here.
val down = acceleration.copy()
down.normalize()
// Magnetic field goes *from* North to South, so reverse it.
val magneticFieldToNorth = magneticField.copy()
magneticFieldToNorth.timesAssign(-1f)
magneticFieldToNorth.normalize()
upPhone = acceleration.normalizedCopy()
val magneticFieldToNorth = magneticField.normalizedCopy()
// This is the vector to magnetic North *along the ground*.
// (The "vector rejection")
magneticNorthPhone =
magneticFieldToNorth - down * (magneticFieldToNorth dot down)
magneticFieldToNorth - upPhone * (magneticFieldToNorth dot upPhone)
magneticNorthPhone.normalize()
upPhone = -down
// East is the cross-product.
magneticEastPhone = magneticNorthPhone * upPhone
}
// The matrix is orthogonal, so transpose it to find its inverse.
// Easiest way to do that is to construct it from row vectors instead
// of column vectors.
axesPhoneInverseMatrix = Matrix3x3(magneticNorthPhone, upPhone, magneticEastPhone, false)
axesPhoneInverseMatrix = Matrix3x3(
magneticNorthPhone, upPhone, magneticEastPhone, false)
}

/**
* Updates the angle between True North and Magnetic North.
*/
private fun updateMagneticCorrection() {
magneticDeclinationCalculator!!.setLocationAndTime(location, timeMillis)
magneticDeclinationCalculator?.setLocationAndTime(location, timeMillis)
}

/**
Expand Down Expand Up @@ -336,18 +346,20 @@ class AstronomerModelImpl(magneticDeclinationCalculator: MagneticDeclinationCalc

companion object {
private val TAG = MiscUtil.getTag(AstronomerModelImpl::class.java)
private val POINTING_DIR_IN_PHONE_COORDS = -Vector3.unitZ()
private val SCREEN_UP_IN_PHONE_COORDS = Vector3.unitY()
private val SCREEN_DOWN_IN_PHONE_COORDS = Vector3.unitX() // TODO(jontayler) this can't be right
private val POINTING_DIR_IN_STANDARD_PHONE_COORDS = -Vector3.unitZ()
private val SCREEN_UP_STANDARD_IN_PHONE_COORDS = Vector3.unitY()
// Some devices like glasses seem to fix the orientation 90 degrees to what we expect.
private val SCREEN_UP_ROTATED_IN_PHONE_COORDS = Vector3.unitX()
// For telescopes where you want the phone strapped to the tube so that you're
// essentially sighting along the long edge of the phone
private val POINTING_DIR_FOR_TELESCOPES = Vector3.unitY()
private val SCREEN_UP_FOR_TELESCOPES = Vector3.unitZ()

private val AXIS_OF_EARTHS_ROTATION = Vector3.unitZ()
private const val MINIMUM_TIME_BETWEEN_CELESTIAL_COORD_UPDATES_MILLIS = 60000L
private const val TOL = 0.01f
}

/**
* @param magneticDeclinationCalculator A calculator that will provide the
* magnetic correction from True North to Magnetic North.
*/
init {
setMagneticDeclinationCalculator(magneticDeclinationCalculator)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public SensorDampingSettings(float damping, int exponent) {
};

private SensorManager manager;
private SensorListener accelerometerSmoother;
private SensorListener compassSmoother;
private SensorEventListener accelerometerSmoother;
private SensorEventListener compassSmoother;
private Provider<PlainSmootherModelAdaptor> modelAdaptorProvider;
private Sensor rotationSensor;
private SharedPreferences sharedPreferences;
Expand Down Expand Up @@ -126,11 +126,18 @@ public void start() {
modelAdaptor,
MAG_DAMPING_SETTINGS[dampingIndex].damping,
MAG_DAMPING_SETTINGS[dampingIndex].exponent);
Sensor accelerometer = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Sensor compass = manager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
if (accelerometer == null || compass == null) {
Log.e(TAG, "Missing accelerometer or compass! Aborting");
return;
}

manager.registerListener(accelerometerSmoother,
SensorManager.SENSOR_ACCELEROMETER,
accelerometer,
sensorSpeed);
manager.registerListener(compassSmoother,
SensorManager.SENSOR_MAGNETIC_FIELD,
compass,
sensorSpeed);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
// limitations under the License.
package com.google.android.stardroid.util.smoothers;

import android.hardware.SensorListener;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.util.Log;

import com.google.android.stardroid.math.MathUtils;
Expand All @@ -28,7 +30,7 @@ public class ExponentiallyWeightedSmoother extends SensorSmoother {
private float alpha;
private int exponent;

public ExponentiallyWeightedSmoother(SensorListener listener, float alpha, int exponent) {
public ExponentiallyWeightedSmoother(SensorEventListener listener, float alpha, int exponent) {
super(listener);
Log.d(TAG, "ExponentionallyWeightedSmoother with alpha = " + alpha + " and exp = " + exponent);
this.alpha = alpha;
Expand All @@ -39,7 +41,11 @@ public ExponentiallyWeightedSmoother(SensorListener listener, float alpha, int e
private float[] current = new float[3];

@Override
public void onSensorChanged(int sensor, float[] values) {
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor sensor = sensorEvent.sensor;
float[] values = sensorEvent.values;


for (int i = 0; i < 3; ++i) {
last[i] = current[i];
float diff = values[i] - last[i];
Expand All @@ -50,7 +56,9 @@ public void onSensorChanged(int sensor, float[] values) {
if (correction > MathUtils.abs(diff) ||
correction < -MathUtils.abs(diff)) correction = diff;
current[i] = last[i] + correction;
sensorEvent.values[i] = current[i];
}
listener.onSensorChanged(sensor, current);

listener.onSensorChanged(sensorEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
package com.google.android.stardroid.util.smoothers;

import android.content.SharedPreferences;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.util.Log;

import com.google.android.stardroid.ApplicationConstants;
Expand All @@ -31,7 +32,7 @@
*
* @author John Taylor
*/
public class PlainSmootherModelAdaptor implements SensorListener {
public class PlainSmootherModelAdaptor implements SensorEventListener {
private static final String TAG = MiscUtil.getTag(PlainSmootherModelAdaptor.class);
private Vector3 magneticValues = ApplicationConstants.INITIAL_SOUTH.copyForJ();
private Vector3 acceleration = ApplicationConstants.INITIAL_DOWN.copyForJ();
Expand All @@ -46,28 +47,25 @@ public class PlainSmootherModelAdaptor implements SensorListener {
}

@Override
public void onSensorChanged(int sensor, float[] values) {
if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
public void onSensorChanged(SensorEvent sensorEvent) {
Sensor sensor = sensorEvent.sensor;
float[] values = sensorEvent.values;
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
acceleration.x = values[0];
acceleration.y = values[1];
acceleration.z = values[2];
} else if (sensor == SensorManager.SENSOR_MAGNETIC_FIELD) {
} else if (sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
magneticValues.x = values[0];
magneticValues.y = values[1];
// The z direction for the mag magneticField sensor is in the opposite
// direction to that for accelerometer, except on some phones that are doing it wrong.
// Yes that's right, the right thing to do is to invert it. So if we reverse that,
// we don't invert it. Got it?
// TODO(johntaylor): this might not be the best place to do this.
magneticValues.z = reverseMagneticZaxis ? values[2] : -values[2];
magneticValues.z = reverseMagneticZaxis ? -values[2] : values[2];
} else {
Log.e(TAG, "Pump is receiving values that aren't accel or magnetic");
}
model.setPhoneSensorValues(acceleration, magneticValues);
}

@Override
public void onAccuracyChanged(int sensor, int accuracy) {
// Do nothing, at present.
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Do nothing
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,23 @@

package com.google.android.stardroid.util.smoothers;

import android.hardware.SensorListener;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;

public abstract class SensorSmoother implements SensorEventListener {

public abstract class SensorSmoother implements SensorListener {
protected SensorEventListener listener;

protected SensorListener listener;

public SensorSmoother(SensorListener listener) {
public SensorSmoother(SensorEventListener listener) {
this.listener = listener;
}

@Override
public void onAccuracyChanged(int sensor, int accuracy) {
// Do Nothing
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Do nothing
}

public abstract void onSensorChanged(int sensor, float[] values);
@Override
public abstract void onSensorChanged(SensorEvent sensor);
}
7 changes: 7 additions & 0 deletions app/src/main/res/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,11 @@
<item translation_description="Extra High sensor damping factor">About right</item>
<item translation_description="Really High sensor damping factor">Laggy</item>
</string-array>
<string-array name="viewing_direction">
<item translation_description="Normal viewing">Normal</item>
<item translation_description="Rotates the screen by 90 degrees">Rotated horizon</item>
<item
translation_description="Shows the sky as pointed at by the long edge of the phone">Pointer mode
</item>
</string-array>
</resources>
8 changes: 8 additions & 0 deletions app/src/main/res/values/help.xml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ which sensors you have. If your phone does not have a gyro, try selecting
&lt;b&gt;If all else fails, send us an email (preferably with a screenshot of your Diagnostics page)
and we\'ll help you figure it out.&lt;/b&gt; &lt;br/&gt;&lt;br/&gt;

&lt;b&gt;Telescope Users&lt;/b&gt;&lt;br/&gt;

Sky Map now includes a prototype \'Pointer Mode\' where the screen will show not what is in
front of the phone, but what you would see if you sighted along the long edge of it. The
purpose is so that you can attach your phone to your telescope tube and have the screen
perpendicular to the direction of the tube. You can enable this in \'settings\' under
\'sensor settings\'.

&lt;b&gt;Credits&lt;/b&gt;&lt;br/&gt;
&lt;i&gt;Engineering, Design and Product Management&lt;/i&gt;&lt;br/&gt;
Brent Bryan, Dominic Widdows, Hector Ouilhet, James Powell,
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/values/notranslate-arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@
<item translation_description="DO NOT TRANSLATE">EXTRA HIGH</item>
<item translation_description="DO NOT TRANSLATE">REALLY HIGH</item>
</string-array>
<string-array name="viewing_direction_values">
<item translation_description="DO NOT TRANSLATE">STANDARD</item>
<item translation_description="DO NOT TRANSLATE">ROTATE90</item>
<item translation_description="DO NOT TRANSLATE">TELESCOPE</item>
</string-array>
</resources>
Loading

0 comments on commit 4b46400

Please sign in to comment.