7 |
--------------------------------------------------------------------------------
/FlightGearMap/res/raw/changelog.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
21 |
22 | $ 2.1
23 | !IMPORTANT! Read http://wiki.flightgear.org/FlightGearMap for installation instructions.
24 | % Version 2.1b
25 | ! The new panel for the B1900D uses a different andatlas.xml
26 | _ 2013-11-20
27 | * Shows airports and navaids on maps.
28 | * New panel: B1900D
29 | * HSI: the glideslope mark is not shown if GS not in range
30 | * New option to switch full screen off
31 | * New option to center (or not) surfaces on screen
32 | * Shows the IP address in the main activity
33 | * Removes last references to Google Maps
34 | * Bug fixing: the comm panel couldn't be selected
35 | % Version 2.1
36 | _ 2013-08-31
37 | * The heading bug in the C337 panel is movable
38 | * New panel: single instrument panel
39 | * Old panel: recovering basic panel, after users' requests
40 | * Bug fixing: The oil pressure in the single engine panel is now correct
41 | * Bug fixing: the manifold pressure in the single engine panel is now correct
42 | * Bug fixing: screens with a 4/3 aspect ratio are now correct.
43 | * Use alternative starter on some aircrafts
44 | $ 2.0
45 | % Version 2.0
46 | _ 2013-05-18
47 | ! YOU MUST UPDATE ANDATLAS.XML TO THE LAST VERSION
48 | * Show changelog dialog (this message!)
49 | * Ads to have some revenue (sorry!)
50 | * New instruments: altitude radar, HSI, RMI, magnets and starter.
51 | * New panel: Cessna 337 Skymaster
52 | * New panel: Comms panel
53 | * Single engine panel: you can configure some of the instruments
54 | * Speed improvements on all surfaces
55 | $ 1.5
56 | % Version 1.5
57 | _ 2012-05-06
58 | ! YOU MUST UPDATE ANDATLAS.XML TO THE LAST VERSION
59 | * Calibrate instruments and controls using the touch screen: NAV1, NAV2, altimeter and lights.
60 | $ 1.4
61 | % Version 1.4
62 | _ 2012-04-29
63 | * New panel: Cessna 172
64 | * Speed and memory improvements: chooses images according to the device's capabilities.
65 | $ END_OF_CHANGE_LOG
66 |
67 |
68 |
--------------------------------------------------------------------------------
/FlightGearMap/res/values-es/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Mapnik
7 | Ciclista
8 | Comunicaciones
9 | Mapquest Aéreo
10 |
11 |
12 |
13 | mapnik
14 | cycle
15 | public_transport
16 | mapquest
17 |
18 |
19 |
20 | Por defecto
21 | Pequeño
22 | Grande
23 | Caza
24 | Bombardero
25 |
26 |
27 |
28 | plane1
29 | plane2
30 | plane3
31 | plane4
32 | plane5
33 |
34 |
35 |
--------------------------------------------------------------------------------
/FlightGearMap/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FlightGearMap
5 | Sin conexión wifi.
6 | FlighGearMap no puede continuar. Por favor, vuelve a intentarlo.
7 | Ejecuta fgfs usando
8 | Atención
9 | Comunicación establecida.
10 | Tiempo de espera sobrepasado.
11 | Opciones…
12 | Salir
13 | Por favor, espera 10 segundos y vuelve a intentarlo.
14 | ¿Actualizaste andatlas.xml en tu PC?
15 | Conectando con FlightGear (máximo, 10 segundos)
16 | Mapa e instrumentos
17 | Solo mapa
18 | Un solo motor (configurable)
19 | Pantalla avanzada
20 | Panel comms
21 | Cambios recientes
22 | Historia
23 | OK
24 | Muestra historia
25 | Cancelar
26 | Radial
27 | En uso
28 | En espera
29 | Intercambiar
30 | Anterior
31 | Siguiente
32 | Instrumento único
33 | Panel básico
34 | Si consideras esta aplicación útil, compra la versión de donación.
35 | Mi dirección IP addres: %. La necesitarás para configurar fgfs. Consulta: http://wiki.flightgear.org/FlightGearMap
36 |
37 |
38 |
--------------------------------------------------------------------------------
/FlightGearMap/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Mapnik
8 | Cycle
9 | Mapquest Aerial
10 | Solid color
11 |
12 |
13 |
14 | mapnik
15 | cycle
16 | mapquest
17 | solid
18 |
19 |
20 |
21 | Default
22 | Tiny
23 | Large
24 | Combat
25 | Bomber
26 |
27 |
28 |
29 | plane1
30 | plane2
31 | plane3
32 | plane4
33 | plane5
34 |
35 |
36 |
37 | Only heading
38 | RMI
39 | HSI
40 |
41 |
42 |
43 | HI
44 | RMI
45 | HSI
46 |
47 |
48 |
49 | Vne=160
50 | Vne=200
51 |
52 |
53 |
54 | asi160
55 | asi200
56 |
57 |
58 |
59 | None
60 | Altitude radar
61 | Manifold pressure
62 |
63 |
64 |
65 | none
66 | radar
67 | manifold
68 |
69 |
70 |
--------------------------------------------------------------------------------
/FlightGearMap/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/FlightGearMap/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FlightGearMap
5 | No wifi connection detected.
6 | The application can\'t continue. Please, restart.
7 | Run fgfs using
8 | Warning
9 | Connection established.
10 | Connection timeout.
11 | Settings…
12 | Quit
13 | Please, wait 10 seconds and try again.
14 | Did you update andatlas.xml in your PC?
15 | Connecting to remote FlightGear (timeout: 10s)
16 | Map and controls
17 | Only map
18 | Seneca II (two engines, 200kts, HSI)
19 | Single engine panel (configurable)
20 | Cessna 337 Skymaster
21 | B1900D
22 | Liquid panel
23 | Comm panel
24 | Recent changes
25 | History
26 | OK
27 | Show full history
28 | Cancel
29 | Radial
30 | In use
31 | Standby
32 | Swap
33 | Prev. instrument
34 | Next instrument
35 | Single instrument
36 | Basic instruments
37 | If you find this app useful, consider buying the donate version
38 | My IP address: %. You will need this to configure fgfs. Check http://wiki.flightgear.org/FlightGearMap
39 |
--------------------------------------------------------------------------------
/FlightGearMap/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
16 |
17 |
25 |
26 |
31 |
32 |
37 |
38 |
43 |
44 |
50 |
51 |
56 |
57 |
58 |
59 |
63 |
64 |
72 |
73 |
81 |
82 |
90 |
91 |
97 |
98 |
99 |
100 |
103 |
104 |
112 |
113 |
120 |
121 |
129 |
130 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/BitmapProvider.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.Hashtable;
6 |
7 | import android.content.Context;
8 | import android.content.res.AssetManager;
9 | import android.graphics.Bitmap;
10 | import android.graphics.BitmapFactory;
11 | import android.graphics.Matrix;
12 |
13 | /** Manages Bitmaps, to prevent loading same images twice.
14 | * @author juanvi
15 | *
16 | */
17 | public class BitmapProvider {
18 | /** key: image names, value: original bitmaps. */
19 | private Hashtable bitmaps;
20 | /** key: image names, value: scaled bitmaps. */
21 | private Hashtable scaledBitmaps;
22 | /** The context of the provider. */
23 | private Context context;
24 | /** Directory inside assets for low quality bitmaps. */
25 | public static final String LOW_QUALITY = "low";
26 | /** Directory inside assets for medium quality bitmaps. */
27 | public static final String MEDIUM_QUALITY = "medium";
28 | /** Directory inside assets for high quality bitmaps. */
29 | public static final String HIGH_QUALITY = "high";
30 | /** The scale of the bitmaps */
31 | private float scale = -1;
32 |
33 | /** Constructor.
34 | * @param ctx The context of the provider.
35 | */
36 | public BitmapProvider(final Context ctx) {
37 | bitmaps = new Hashtable();
38 | scaledBitmaps = new Hashtable();
39 | context = ctx;
40 | }
41 |
42 | /** Scale the currently available bitmaps.
43 | *
44 | * @param scale scale=1 original size
45 | */
46 | public void setScale(float s) {
47 | Matrix matrix = new Matrix();
48 | this.scale = s;
49 | matrix.setScale(scale, scale);
50 | // remove from memory previous versions of the bitmaps
51 | for(Bitmap b: this.scaledBitmaps.values()) {
52 | b.recycle();
53 | }
54 | scaledBitmaps.clear();
55 |
56 | for (String imgName: bitmaps.keySet()) {
57 | Bitmap b = bitmaps.get(imgName);
58 | scaledBitmaps.put(imgName, Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true));
59 | }
60 | }
61 |
62 | public float getScale() {
63 | return scale;
64 | }
65 |
66 | /**
67 | * Load a Bitmap and cache it.
68 | * In memory constraint devices, high quality bitmaps cannot be loaded and OutOfMemoryError is triggered here.
69 | *
70 | * @param dir The directory where search the Bitmap. One of LOW_QUALITY/MEDIUM_QUALITY/HIGH_QUALITY
71 | * @param imgName The name of the image to load
72 | * @return The Bitmap with that name.
73 | */
74 | public Bitmap getBitmap(String dir, String imgName) {
75 | if (!bitmaps.containsKey(imgName)) {
76 | AssetManager mng = context.getAssets();
77 | try {
78 | Bitmap l = BitmapFactory.decodeStream(mng.open(dir + File.separator + imgName));
79 | bitmaps.put(imgName, l);
80 | } catch (IOException e) {
81 | return null;
82 | }
83 | }
84 | return bitmaps.get(imgName);
85 | }
86 |
87 | /** Returns a scaled image. Call to setScale before this method.
88 | * @param imgName The name of the image to return.
89 | * @return The scaled version of the image.
90 | */
91 | public Bitmap getScaledBitmap(String imgName) {
92 | if (scaledBitmaps.containsKey(imgName)) {
93 | return scaledBitmaps.get(imgName);
94 | }
95 | return null;
96 | }
97 |
98 | /** Recycle the Bitmaps and clear the inner arrays. */
99 | public void recycle() {
100 | for (Bitmap b: bitmaps.values()) {
101 | b.recycle();
102 | }
103 | for (Bitmap b: scaledBitmaps.values()) {
104 | b.recycle();
105 | }
106 | bitmaps.clear();
107 | scaledBitmaps.clear();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/FGFSConnection.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear;
2 |
3 | //FGFSConnection.java - client library for the FlightGear flight simulator.
4 | //Started June 2002 by David Megginson, david@megginson.com
5 | //This library is in the Public Domain and comes with NO WARRANTY.
6 |
7 | import java.io.BufferedReader;
8 | import java.io.InputStreamReader;
9 | import java.io.IOException;
10 | import java.io.PrintWriter;
11 |
12 | import java.net.Socket;
13 |
14 |
15 | /**
16 | * A connection to a running instance of FlightGear.
17 | *
18 | *
This class currently uses the FlightGear telnet interface,
19 | * though it may be modified to use a different TCP/IP interface in
20 | * the future. Client applications can use this library to examine
21 | * and modify internal FlightGear properties.
22 | *
23 | *
To start FlightGear with the telnet server activated, use a
24 | * command like this (to listen on port 9000):
25 | *
26 | *
27 | * fgfs --telnet=9000
28 | *
29 | *
30 | *
Then create a connection to FlightGear from your Java client
31 | * application:
Create a new connection to a running FlightGear program.
62 | * The program must have been started with the --telnet=<port>
63 | * command-line option.
64 | *
65 | * @param host The host name or IP address to connect to.
66 | * @param port The port number where FlightGear is listening.
67 | * @exception IOException If it is not possible to connect to
68 | * a FlightGear process.
69 | */
70 | public FGFSConnection (String host, int port, int timeout)
71 | throws IOException
72 | {
73 | socket = new Socket(host, port);
74 | socket.setSoTimeout(timeout);
75 | in =
76 | new BufferedReader(new InputStreamReader(socket.getInputStream()));
77 | out = new PrintWriter(socket.getOutputStream(), true);
78 | out.println("data\r");
79 | }
80 |
81 |
82 |
83 | ////////////////////////////////////////////////////////////////////
84 | // Primitive getter and setter.
85 | ////////////////////////////////////////////////////////////////////
86 |
87 |
88 | /**
89 | * Close the connection to FlightGear.
90 | *
91 | *
The client application should always invoke this method when
92 | * it has finished with a connection, to allow cleanup.
93 | *
94 | * @exception IOException If there is an error closing the
95 | * connection.
96 | */
97 | public synchronized void close ()
98 | throws IOException
99 | {
100 | out.println("quit\r");
101 | in.close();
102 | out.close();
103 | socket.close();
104 | }
105 |
106 | public boolean isClosed() {
107 | return this.socket == null || this.socket.isClosed();
108 | }
109 |
110 |
111 | /**
112 | * Get the raw string value for a property.
113 | *
114 | *
This is the primitive method for all property lookup;
115 | * everything comes in as a string, and is only later converted by
116 | * methods like {@link #getDouble(String)}. As a result, if you
117 | * need the value as a string anyway, it makes sense to use this
118 | * method directly rather than forcing extra conversions.
119 | *
120 | * @param name The FlightGear property name to look up.
121 | * @return The property value as a string (non-existant properties
122 | * return the empty string).
123 | * @exception IOException If there is an error communicating with
124 | * FlightGear or if the connection is lost.
125 | * @see #getBoolean(String)
126 | * @see #getInt(String)
127 | * @see #getLong(String)
128 | * @see #getFloat(String)
129 | * @see #getDouble(String)
130 | */
131 | public synchronized String get (String name)
132 | throws IOException
133 | {
134 | out.println("get " + name + '\r');
135 | return in.readLine();
136 | }
137 |
138 |
139 | /**
140 | * Set the raw string value for a property.
141 | *
142 | *
This is the primitive method for all property modification;
143 | * everything goes out as a string, after it has been converted by
144 | * methods like {@link #setDouble(String,double)}. As a result, if
145 | * you have the value as a string already, it makes sense to use
146 | * this method directly rather than forcing extra conversions.
147 | *
148 | * @param name The FlightGear property name to modify or create.
149 | * @param value The new value for the property, as a string.
150 | * @exception IOException If there is an error communicating with
151 | * FlightGear or if the connection is lost.
152 | * @see #setBoolean(String,boolean)
153 | * @see #setInt(String,int)
154 | * @see #setLong(String,long)
155 | * @see #setFloat(String,float)
156 | * @see #setDouble(String,double)
157 | */
158 | public synchronized void set (String name, String value)
159 | throws IOException
160 | {
161 | out.println("set " + name + ' ' + value + '\r');
162 | }
163 |
164 |
165 |
166 | ////////////////////////////////////////////////////////////////////
167 | // Derived getters and setters.
168 | ////////////////////////////////////////////////////////////////////
169 |
170 |
171 | /**
172 | * Get a property value as a boolean.
173 | *
174 | * @param name The property name to look up.
175 | * @return The property value as a boolean.
176 | * @see #get(String)
177 | */
178 | public boolean getBoolean (String name)
179 | throws IOException
180 | {
181 | return get(name).equals("true");
182 | }
183 |
184 |
185 | /**
186 | * Get a property value as an integer.
187 | *
188 | * @param name The property name to look up.
189 | * @return The property value as an int.
190 | * @see #get(String)
191 | */
192 | public int getInt (String name, int def)
193 | throws IOException
194 | {
195 | try{
196 | return Integer.parseInt(get(name));
197 | } catch (NumberFormatException e) {
198 | return def;
199 | }
200 | }
201 |
202 |
203 | /**
204 | * Get a property value as a long.
205 | *
206 | * @param name The property name to look up.
207 | * @return The property value as a long.
208 | * @see #get(String)
209 | */
210 | public long getLong (String name, long def)
211 | throws IOException
212 | {
213 | try {
214 | return Long.parseLong(get(name));
215 | } catch (NumberFormatException e) {
216 | return def;
217 | }
218 | }
219 |
220 |
221 | /**
222 | * Get a property value as a float.
223 | *
224 | * @param name The property name to look up.
225 | * @param def The default value if the number cannot be converted.
226 | * @return The property value as a float.
227 | * @see #get(String)
228 | */
229 | public float getFloat (String name, float def)
230 | throws IOException
231 | {
232 | try {
233 | return Float.parseFloat(get(name));
234 | } catch (NumberFormatException e) {
235 | return def;
236 | }
237 | }
238 |
239 |
240 | /**
241 | * Get a property value as a double.
242 | *
243 | * @param name The property name to look up.
244 | * @return The property value as a double.
245 | * @see #get(String)
246 | */
247 | public double getDouble (String name, double def)
248 | throws IOException
249 | {
250 | try{
251 | return Double.parseDouble(get(name));
252 | } catch (NumberFormatException e) {
253 | return def;
254 | }
255 | }
256 |
257 |
258 | /**
259 | * Set a property value from a boolean.
260 | *
261 | * @param name The property name to create or modify.
262 | * @param value The new property value as a boolean.
263 | * @see #set(String,String)
264 | */
265 | public void setBoolean (String name, boolean value)
266 | throws IOException
267 | {
268 | set(name, (value ? "true" : "false"));
269 | }
270 |
271 |
272 | /**
273 | * Set a property value from an int.
274 | *
275 | * @param name The property name to create or modify.
276 | * @param value The new property value as an int.
277 | * @see #set(String,String)
278 | */
279 | public void setInt (String name, int value)
280 | throws IOException
281 | {
282 | set(name, Integer.toString(value));
283 | }
284 |
285 |
286 | /**
287 | * Set a property value from a long.
288 | *
289 | * @param name The property name to create or modify.
290 | * @param value The new property value as a long.
291 | * @see #set(String,String)
292 | */
293 | public void setLong (String name, long value)
294 | throws IOException
295 | {
296 | set(name, Long.toString(value));
297 | }
298 |
299 |
300 | /**
301 | * Set a property value from a float.
302 | *
303 | * @param name The property name to create or modify.
304 | * @param value The new property value as a float.
305 | * @see #set(String,String)
306 | */
307 | public void setFloat (String name, float value)
308 | throws IOException
309 | {
310 | set(name, Float.toString(value));
311 | }
312 |
313 |
314 | /**
315 | * Set a property value from a double.
316 | *
317 | * @param name The property name to create or modify.
318 | * @param value The new property value as a double.
319 | * @see #set(String,String)
320 | */
321 | public void setDouble (String name, double value)
322 | throws IOException
323 | {
324 | set(name, Double.toString(value));
325 | }
326 |
327 |
328 |
329 | ////////////////////////////////////////////////////////////////////
330 | // Internal state.
331 | ////////////////////////////////////////////////////////////////////
332 |
333 | private Socket socket;
334 | private BufferedReader in;
335 | private PrintWriter out;
336 |
337 | }
338 |
339 | //end of FGFSConnection.java
340 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/MyBitmap.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | /** A wrap over a Bitmap to manage images before loading them */
6 | public class MyBitmap {
7 | private int width;
8 | private int height;
9 | private int xo;
10 | private int yo;
11 | private Bitmap b;
12 | private String file;
13 | public MyBitmap(String file, int xo, int yo, int w, int h) {
14 | this.xo = xo;
15 | this.yo = yo;
16 | this.height = h;
17 | this.width = w;
18 | b = null;
19 | this.file = file;
20 | }
21 | public void updateBitmap(BitmapProvider bitmapProvider, int gridSize) {
22 | if (file != null) {
23 | try {
24 | Bitmap original = bitmapProvider.getScaledBitmap(this.file);
25 | if ( xo > -1) {
26 | float s = bitmapProvider.getScale();
27 | b = Bitmap.createBitmap(
28 | original,
29 | (int)(this.xo * s * gridSize / 512), (int)(this.yo * s * gridSize / 512),
30 | (int)(this.width * s * gridSize / 512), (int)(this.height * s * gridSize / 512));
31 | } else {
32 | b = bitmapProvider.getScaledBitmap(this.file);
33 | }
34 | } catch (NullPointerException e) {
35 | MyLog.w(MyBitmap.class, "Null bitmap: " + this.file);
36 | b = null;
37 | } catch (IllegalArgumentException e) {
38 | MyLog.w(MyBitmap.class, "Bitmap out of bounds: " + this.file);
39 | b = null;
40 | }
41 | }
42 | }
43 | public int getHeight() {
44 | return height;
45 | }
46 | public int getWidth() {
47 | return width;
48 | }
49 | public Bitmap getScaledBitmap() {
50 | return b;
51 | }
52 | public String getFile() {
53 | return this.file;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/MyLog.java:
--------------------------------------------------------------------------------
1 |
2 | package com.juanvvc.flightgear;
3 |
4 | import java.io.PrintWriter;
5 | import java.io.StringWriter;
6 | import java.io.Writer;
7 |
8 | import android.util.Log;
9 |
10 | /** Use this class instead of android.util.Log: simplify the process of uploading to Google Play
11 | * @author juanvi
12 | */
13 | public class MyLog{
14 | private static boolean DEBUG=true;
15 |
16 | public static void setDebug(boolean d) {
17 | MyLog.DEBUG = d;
18 | }
19 | public static boolean isDebug() {
20 | return MyLog.DEBUG;
21 | }
22 |
23 | public static void i(Object o, String msg){
24 | if(DEBUG) Log.i(o.getClass().getSimpleName(), msg);
25 | }
26 | public static void d(Object o, String msg){
27 | if(DEBUG) Log.d(o.getClass().getSimpleName(), msg);
28 | }
29 | public static void v(Object o, String msg){
30 | if(DEBUG) Log.v(o.getClass().getSimpleName(), msg);
31 | }
32 | public static void e(Object o, String msg){
33 | if(DEBUG) Log.e(o.getClass().getSimpleName(), msg);
34 | }
35 | public static void w(Object o, String msg){
36 | if(DEBUG) Log.e(o.getClass().getSimpleName(), msg);
37 | }
38 |
39 | public static void i(String t, String msg){
40 | if(DEBUG) Log.i(t, msg);
41 | }
42 | public static void d(String t, String msg){
43 | if(DEBUG) Log.d(t, msg);
44 | }
45 | public static void v(String t, String msg){
46 | if(DEBUG) Log.v(t, msg);
47 | }
48 | public static void e(String t, String msg){
49 | if(DEBUG) Log.e(t, msg);
50 | }
51 | public static void w(String t, String msg){
52 | if(DEBUG) Log.e(t, msg);
53 | }
54 |
55 | public static String stackToString(Exception e) {
56 | Writer result = new StringWriter();
57 | PrintWriter printWriter = new PrintWriter(result);
58 | e.printStackTrace(printWriter);
59 | return result.toString();
60 | }
61 | }
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/PlaneData.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear;
2 |
3 | import java.util.Date;
4 |
5 | /** Models the data that FlightGear sends.
6 | * @author juanvi
7 | *
8 | */
9 | public class PlaneData {
10 | String[] data;
11 | String[] outData;
12 | private Date date = new Date();
13 | private MovingAverage[] averages;
14 |
15 | public PlaneData() {
16 | data = null;
17 | this.averages = new MovingAverage[ALTITUDE_AGL+1];
18 | }
19 |
20 | public void parse(final String input) {
21 | // strip string with new line
22 | String realInput = input.substring(0, input.indexOf("\n"));
23 | data = realInput.split(":");
24 |
25 | date = new Date();
26 |
27 | // check that we have the desired number of parameters
28 | // just read the last data. If throws IndexOutOfBounds, the
29 | // other extreme is sending wrong data
30 | getFloat(ALTITUDE_AGL);
31 | }
32 |
33 | public static final int SPEED = 0; // speed, in knots
34 | public static final int RPM = 1; // RPM
35 | public static final int HEADING_MOV = 2; // Magnetic heading, in degrees
36 | public static final int ALTITUDE = 3; // altitude, in feet, according to the instruments
37 | public static final int CLIMB_RATE = 4; // rate of climb, in feet per second
38 | public static final int PITCH = 5; // pitch, in degrees
39 | public static final int ROLL = 6; // roll, in degrees
40 | public static final int LATITUDE = 7; // latitude, in degrees
41 | public static final int LONGITUDE = 8; // longitude, in degrees
42 | public static final int SECONDS = 9; // seconds from GMT midnight
43 | public static final int TURN_RATE = 10; // turn rate, in turns per 2min
44 | public static final int SLIP = 11; // slip skid, in ??
45 | public static final int HEADING = 12; // Heading in degrees, according to the instruments
46 | public static final int FUEL1 = 13; // fuel in first tank, in us gals
47 | public static final int FUEL2 = 14; // fuel in second tank, in us gals
48 | public static final int OIL_PRESS = 15; // oil pressure in psi
49 | public static final int OIL_TEMP = 16; // oil temperature in degf
50 | public static final int AMP = 17; // amperes
51 | public static final int VOLT = 18; // voltage
52 | public static final int NAV1_TO = 19; // true if the to flag is set in NAV1
53 | public static final int NAV1_FROM = 20; // true if the from flag is set in NAV1
54 | public static final int NAV1_DEFLECTION = 21; // needle deflection in NAV1
55 | public static final int NAV1_SEL_RADIAL = 22; // selected radial in NAV1
56 | public static final int NAV2_TO = 23; // true if the to flag is set in NAV2
57 | public static final int NAV2_FROM = 24; // true if the from flag is set in NAV2
58 | public static final int NAV2_DEFLECTION = 25; // needle deflection in NAV2
59 | public static final int NAV2_SEL_RADIAL = 26; // selected radial in NAV2
60 | public static final int ADF_DEFLECTION = 27;
61 | public static final int ELEV_TRIM = 28;
62 | public static final int FLAPS = 29;
63 | public static final int GS1_DEFLECTION = 30; // normalized needle deflection (only NAV1)
64 | public static final int GS1_INRANGE = 31; // true if GS in range (only NAV1)
65 | public static final int DME = 32;
66 | public static final int DME_SPEED = 33;
67 | public static final int RPM2 = 34;
68 | public static final int MANIFOLD = 35;
69 | public static final int MANIFOLD2 = 36;
70 | public static final int CHT1_TEMP = 37;
71 | public static final int CHT2_TEMP = 38;
72 | public static final int OIL2_PRESS = 39;
73 | public static final int OIL2_TEMP = 40;
74 | public static final int HEADING_BUG = 41;
75 | public static final int NAV1_HEADING = 42; // heading to the NAV station
76 | public static final int ALTITUDE_AGL = 43;
77 |
78 | // These are used in the B1900D, a turboprop. Notice that indexes are repeated!
79 | public static final int FUEL_FLOW1 = CHT1_TEMP;
80 | public static final int FUEL_FLOW2 = CHT2_TEMP;
81 | public static final int PROP_ENGINE1 = MANIFOLD;
82 | public static final int PROP_ENGINE2 = MANIFOLD2;
83 | public static final int TORQUE1 = RPM;
84 | public static final int TORQUE2 = RPM2;
85 | public static final int TURBINE1 = AMP;
86 | public static final int TURBINE2 = VOLT;
87 | public static final int VNE_SPEED = ALTITUDE_AGL;
88 | public static final int NAV2_HEADING = NAV1_HEADING;
89 | // since oil press doesn't seem to be right on the b1900d, use the slot of ITT
90 | public static final int ITT1 = OIL_PRESS;
91 | public static final int ITT2 = OIL2_PRESS;
92 |
93 |
94 |
95 | public int getInt(int i) {
96 | if (data == null) {
97 | return 0;
98 | }
99 | return Integer.valueOf(data[i]);
100 | }
101 |
102 | public float getFloat(int i) {
103 | if (data == null) {
104 | return 0;
105 | }
106 | MovingAverage ma = this.averages[i];
107 | if (ma==null) {
108 | return Float.valueOf(data[i]);
109 | } else {
110 | return ma.getData(Float.valueOf(data[i]));
111 | }
112 | }
113 |
114 | public String getString(int i) {
115 | if (data == null) {
116 | return "";
117 | }
118 | return data[i];
119 | }
120 |
121 | public boolean getBool(int i) {
122 | if (data == null) {
123 | return false;
124 | }
125 | return data[i].equals("1");
126 | }
127 |
128 | public Date getDate() {
129 | return date;
130 | }
131 |
132 | public boolean hasData() {
133 | return data != null;
134 | }
135 |
136 | /**
137 | * Sets or removes a MovingAverage filter for a specific index
138 | * @param index The index of data to set the filter
139 | * @param set set or remove the filter.
140 | */
141 | public void setMovingAverage(int index, boolean set) {
142 | if (set) {
143 | this.averages[index] = new MovingAverage();
144 | } else {
145 | this.averages[index] = null;
146 | }
147 | }
148 |
149 | /** This filter smoothes the data to prevent very fast changes */
150 | private class MovingAverage {
151 | private static final int SIZE=5;
152 | private float[] buffer = null;
153 | private int current = 0;
154 |
155 | public float getData(float v) {
156 | if ( buffer == null) {
157 | buffer = new float[SIZE];
158 | for(int i=0; i Android 3.0)
16 | // // Display the fragment as the main content.
17 | // getFragmentManager().beginTransaction()
18 | // .replace(android.R.id.content, new SettingsFragment())
19 | // .commit();
20 | //
21 | // }
22 | //
23 | // public static class SettingsFragment extends PreferenceFragment {
24 | // @Override
25 | // public void onCreate(Bundle savedInstanceState) {
26 | // super.onCreate(savedInstanceState);
27 | //
28 | // // Load the preferences from an XML resource
29 | // addPreferencesFromResource(R.xml.preferences);
30 | // }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/SingleInstrumentActivity.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear;
2 |
3 | import android.content.SharedPreferences;
4 | import android.os.Bundle;
5 | import android.preference.PreferenceManager;
6 | import android.view.View;
7 | import android.view.View.OnClickListener;
8 | import android.view.Window;
9 | import android.view.WindowManager;
10 | import android.widget.Button;
11 |
12 | import com.juanvvc.flightgear.instruments.InstrumentType;
13 | import com.juanvvc.flightgear.maps.MapOverlay;
14 | import com.juanvvc.flightgear.panels.Cessna172;
15 | import com.juanvvc.flightgear.panels.PanelView;
16 |
17 | /** A simplification of InstrumentActivity to show only one instrument, and allow chaging the instrument */
18 | public class SingleInstrumentActivity extends PanelActivity implements OnClickListener{
19 |
20 | private int currentInstrument = 0;
21 | private static int MAX_INSTRUMENT = 8;
22 |
23 | public void onCreate(Bundle savedInstanceState) {
24 | // Initialize planaOverlay to bypass some initializations in our parent's constructor
25 | planeOverlay = new MapOverlay(this);
26 | super.onCreate(savedInstanceState);
27 |
28 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
29 | if (sp.getBoolean("fullscreen", true)) {
30 | requestWindowFeature(Window.FEATURE_NO_TITLE);
31 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
32 | }
33 |
34 | this.setContentView(R.layout.singleinstrument);
35 | this.panelView = (PanelView)this.findViewById(R.id.panel);
36 | this.mapView = null;
37 |
38 | Button b = (Button) this.findViewById(R.id.prev_instrument);
39 | b.setOnClickListener(this);
40 | b = (Button) this.findViewById(R.id.next_instrument);
41 | b.setOnClickListener(this);
42 |
43 | // set instruments centered or not
44 | boolean centered = sp.getBoolean("center_instruments", true);
45 | if ( this.panelView != null ) {
46 | this.panelView.setCenterInstruments(centered);
47 | }
48 | }
49 |
50 |
51 | @Override
52 | protected void onStart() {
53 | super.onStart();
54 |
55 | // get the current instrument from preferences
56 | SharedPreferences sp = this.getPreferences(MODE_PRIVATE);
57 | currentInstrument = sp.getInt("current_instrument", 0);
58 | currentInstrument = Math.abs(currentInstrument%MAX_INSTRUMENT);
59 |
60 | this.setInstrument(currentInstrument);
61 | }
62 |
63 | @Override
64 | protected void onPause() {
65 | super.onPause();
66 |
67 | // save the current instrument into the preferences
68 | SharedPreferences sp = this.getPreferences(MODE_PRIVATE);
69 | SharedPreferences.Editor ed = sp.edit();
70 | ed.putInt("current_instrument", currentInstrument);
71 | ed.commit();
72 | }
73 |
74 |
75 | // change the current instrument
76 | public void onClick(View v) {
77 | if (v.getId() == R.id.prev_instrument) {
78 | if (currentInstrument <= 0) {
79 | currentInstrument = MAX_INSTRUMENT;
80 | }
81 | currentInstrument = currentInstrument - 1;
82 | } else if (v.getId() == R.id.next_instrument) {
83 | currentInstrument = currentInstrument + 1;
84 | if (currentInstrument >= MAX_INSTRUMENT) {
85 | currentInstrument = 0;
86 | }
87 | }
88 |
89 | setInstrument(currentInstrument);
90 | }
91 |
92 | private void setInstrument(int i) {
93 |
94 | MyLog.i(this, "Setting instrument: " + i);
95 |
96 | panelView = (PanelView) findViewById(R.id.panel);
97 |
98 | panelView.setVisibility(View.VISIBLE);
99 |
100 | switch(i) {
101 | case 0: panelView.setInstrument(Cessna172.createInstrument(InstrumentType.SPEED, this, 0, 0)); break;
102 | case 1: panelView.setInstrument(Cessna172.createInstrument(InstrumentType.ALTIMETER, this, 0, 0)); break;
103 | case 2: panelView.setInstrument(Cessna172.createInstrument(InstrumentType.HEADING, this, 0, 0)); break;
104 | case 3: panelView.setInstrument(Cessna172.createInstrument(InstrumentType.ATTITUDE, this, 0, 0)); break;
105 | case 4: panelView.setInstrument(Cessna172.createInstrument(InstrumentType.CLIMB_RATE, this, 0, 0)); break;
106 | case 5: panelView.setInstrument(Cessna172.createInstrument(InstrumentType.NAV1, this, 0, 0)); break;
107 | case 6: panelView.setInstrument(Cessna172.createInstrument(InstrumentType.ADF, this, 0, 0)); break;
108 | case 7: panelView.setInstrument(Cessna172.createInstrument(InstrumentType.HSI1, this, 0, 0)); break;
109 | }
110 |
111 | panelView.invalidate();
112 |
113 | if (this.calibratableManager != null) {
114 | this.calibratableManager.empty();
115 | panelView.postCalibratableSurfaceManager(this.calibratableManager);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/CalibratableRotateSurface.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import java.io.IOException;
4 |
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 |
8 | import com.juanvvc.flightgear.FGFSConnection;
9 | import com.juanvvc.flightgear.MyBitmap;
10 | import com.juanvvc.flightgear.MyLog;
11 |
12 | /** A surface that is rotated according to the telnet connection, and can be calibrated. */
13 | public class CalibratableRotateSurface extends Surface {
14 | private Matrix m;
15 | /** The property to read from the remote fgfs */
16 | private String prop;
17 | /** If true, the value is wrapped (after max, it is min again) */
18 | private boolean wrapped = false;
19 | /** The property to read from PlaneData, if positive. */
20 | protected int propIdx;
21 | /** ration center */
22 | protected float rcx;
23 | protected float rcy;
24 | /** min value, and its angle. */
25 | private float min, amin;
26 | /** max value, and its angle. */
27 | private float max, amax;
28 | // The final position of the surface, scale and gridsize considered (calculated in onBitmapChanged())
29 | private float finalx, finaly;
30 | // The final position of the rotation center, scale and gridsize considered (calculated in onBitmapChanged())
31 | private float finalrx, finalry;
32 |
33 | private float value = 0;
34 | private boolean moving = false;
35 | private boolean dirtyValue = false;
36 | // private float angle_start = 0;
37 | // private float angle_moved = 0;
38 | private float lastx, lasty;
39 |
40 | private static final int TOUCHABLE_WIDTH = 256;
41 | private static final float ROTATION_SCALE = 0.3f;
42 |
43 | private boolean firstRead = true;
44 |
45 | /**
46 | * @param file The file of the image (does not include directory)
47 | * @param x Horizontal position of the surface inside the instrument
48 | * @param y Horizontal position of the surface inside the instrument
49 | * @param prop The path to the property to read from the remote FGFSConnetion
50 | * @param wrap Wrap the value at the end of the range, or keep it inside limits
51 | * @param propIdx A property index to follow (if any. If not, set to -1)
52 | * @param rscale Scale of the data (usually 1: do not modify the data)
53 | * @param rcx Rotation center (inside the instrument)
54 | * @param rcy Rotation center (inside the instrument)
55 | * @param min Minimum value of the data
56 | * @param amin Angle that corresponds to the minimum value
57 | * @param max Max value of the data
58 | * @param amax Angle that corresponds to the max value.
59 | */
60 | public CalibratableRotateSurface(
61 | MyBitmap bitmap, float x, float y,
62 | String prop, boolean wrap,
63 | int propIdx,
64 | int rcx, int rcy,
65 | float min, float amin, float max, float amax) {
66 | super(bitmap, x, y);
67 | m = new Matrix();
68 | this.rcx = rcx;
69 | this.rcy = rcy;
70 | this.min = min;
71 | this.amin = amin;
72 | this.max = max;
73 | this.amax = amax;
74 | this.wrapped = wrap;
75 | this.propIdx = propIdx;
76 | this.prop = prop;
77 | }
78 |
79 | /**
80 | * @param v value angle
81 | * @return The angle to rotate the drawable to match that value
82 | */
83 | protected float getRotationAngle(float v) {
84 | if (v < min) {
85 | v = min;
86 | } else if (v > max) {
87 | v = max;
88 | }
89 | return (v - min) * (amax - amin) / (max - min) + amin;
90 | }
91 |
92 | @Override
93 | public void onBitmapChanged() {
94 | final float realscale = parent.getScale() * parent.getGridSize();
95 | final float col = parent.getCol();
96 | final float row = parent.getRow();
97 | finalx = (col + relx ) * realscale;
98 | finaly = (row + rely ) * realscale;
99 | finalrx = (col + rcx / 512f ) * realscale;
100 | finalry = (row + rcy / 512f ) * realscale;
101 | }
102 |
103 | @Override
104 | public void onDraw(Canvas c) {
105 | if (planeData == null || !planeData.hasData() || bitmap == null) {
106 | return;
107 | }
108 |
109 | if (!this.dirtyValue && this.propIdx > -1 && this.planeData != null && planeData.hasData()) {
110 | value = planeData.getFloat(this.propIdx);
111 | }
112 |
113 | m.reset();
114 | m.setTranslate(finalx, finaly);
115 | m.postRotate(getRotationAngle(value), finalrx, finalry);
116 | c.drawBitmap(bitmap.getScaledBitmap(), m, null);
117 | }
118 |
119 | @Override
120 | public boolean youControl(float x, float y) {
121 | // note that the user must rotate the movement centered on the rotation point.
122 | // In most instruments, this is different from the real calibration wheel, but I think
123 | // that this way it is much more comfortable
124 | return Math.abs(x - this.rcx) < TOUCHABLE_WIDTH && Math.abs(y - this.rcy) < TOUCHABLE_WIDTH;
125 | }
126 |
127 | @Override
128 | public void onMove(float x, float y, boolean end) {
129 | if (end && !moving) {
130 | return;
131 | }
132 |
133 | float a1, a2, da;
134 |
135 |
136 | if (!moving) {
137 | moving = true;
138 | // angle_start = (float)Math.atan2(x - rcx, y - rcy);
139 | lastx = x;
140 | lasty = y;
141 | } else {
142 | a1 = (float)Math.atan2(lastx - rcx, lasty - rcy);
143 | a2 = (float)Math.atan2(x - rcx, y - rcy);
144 | da = a2 - a1;
145 | float rotateAngle = 0; //value; //this.getDrawableRotationAngle(value);
146 | if (Math.abs(da) < 1) { // do not consider the discontinuity in 0-360. This is a small error. Noticeable? Don't think so.
147 | //rotateAngle += (da * 180 / Math.PI) * ROTATION_SCALE;
148 | rotateAngle += (da * 180 / Math.PI);
149 | }
150 | lastx = x;
151 | lasty = y;
152 | if (end) {
153 | moving = false;
154 | }
155 |
156 | value += rotateAngle * ROTATION_SCALE * (this.max - this.min) / 360;
157 |
158 | if (value > max) {
159 | if (this.wrapped) {
160 | value = min + (value - max);
161 | } else {
162 | value = max;
163 | }
164 | } else if (value < min) {
165 | if (this.wrapped) {
166 | value = max - Math.abs(min - value);
167 | } else {
168 | value = min;
169 | }
170 | }
171 |
172 | this.dirtyValue = true;
173 | }
174 | }
175 |
176 | @Override
177 | public void postCalibratableSurfaceManager(CalibratableSurfaceManager cs) {
178 | cs.register(this);
179 | firstRead = true;
180 | }
181 |
182 |
183 | @Override
184 | public boolean isDirty() {
185 | return dirtyValue || firstRead;
186 | }
187 |
188 | @Override
189 | public void update(FGFSConnection conn) throws IOException {
190 | if (conn == null || conn.isClosed()) {
191 | return;
192 | }
193 |
194 | if (this.prop == null) {
195 | // if prop is null, we cannot send/receive our value to the server. We are done.
196 | dirtyValue = false;
197 | firstRead = false;
198 | return;
199 | }
200 |
201 | // if our value is not dirty, read from the remote fgfs
202 | if (!dirtyValue || firstRead) {
203 | try {
204 | value = conn.getFloat(prop, 0f);
205 | firstRead = false;
206 | } catch (NumberFormatException e) {
207 | MyLog.e(this, prop + ": " + e.toString());
208 | }
209 | } else {
210 | // if dirty, push the value
211 | conn.setFloat(prop, value);
212 | dirtyValue = false;
213 | }
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/CalibratableSurfaceManager.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import java.io.IOException;
4 | import java.util.ArrayList;
5 |
6 | import android.content.SharedPreferences;
7 |
8 | import com.juanvvc.flightgear.FGFSConnection;
9 | import com.juanvvc.flightgear.PanelActivity;
10 | import com.juanvvc.flightgear.MyLog;
11 |
12 | public class CalibratableSurfaceManager extends Thread {
13 | private ArrayList surfaces;
14 | private SharedPreferences sp;
15 |
16 | public CalibratableSurfaceManager(SharedPreferences sp) {
17 | this.sp = sp;
18 | surfaces = new ArrayList();
19 | }
20 |
21 | public void register(Surface sc) {
22 | synchronized(surfaces) {
23 | this.surfaces.add(sc);
24 | }
25 | }
26 |
27 | public void empty() {
28 | synchronized(surfaces) {
29 | this.surfaces.clear();
30 | }
31 | }
32 |
33 | public void run() {
34 | FGFSConnection conn = null;
35 | boolean cancelled = false;
36 | int waitPeriod = 5000;
37 | int port = 9000;
38 | String fgfsIP = "192.168.1.2";
39 |
40 | // read preferences
41 | try {
42 | waitPeriod = Integer.valueOf(sp.getString("update_period", "500"));
43 | // check limits
44 | waitPeriod = Math.max(waitPeriod, 500);
45 | } catch (NumberFormatException e) {
46 | MyLog.w(this, "Config error: wrong update_period=" + sp.getString("update_period", "default") +".");
47 | waitPeriod = 5000;
48 | }
49 | try {
50 | port = Integer.valueOf(sp.getString("telnet_port", "9000"));
51 | // check limits
52 | port = Math.max(port, 1);
53 | } catch (ClassCastException e) {
54 | MyLog.w(this, "Config error: wrong port=" + sp.getString("telnet_port", "default"));
55 | port = 9000;
56 | }
57 | fgfsIP = sp.getString("fgfs_ip", "192.168.1.2");
58 |
59 | MyLog.i(this, "Telnet: " + fgfsIP + ":" + port + " " + waitPeriod + "ms");
60 |
61 | try {
62 | MyLog.e(this, "Trying telnet connection to " + fgfsIP + ":" + port);
63 | conn = new FGFSConnection(fgfsIP, port, PanelActivity.SOCKET_TIMEOUT);
64 | MyLog.d(this, "Upwards connection ready");
65 | } catch (IOException e) {
66 | MyLog.w(this, e.toString());
67 | conn = null;
68 | return;
69 | }
70 |
71 | while (!cancelled) {
72 | try{
73 | synchronized(surfaces) {
74 | for(Surface cs: surfaces) {
75 | if (cs.isDirty()) {
76 | cs.update(conn);
77 | }
78 | }
79 | }
80 | Thread.sleep(waitPeriod);
81 | cancelled = conn.isClosed();
82 | } catch (InterruptedException e) {
83 | cancelled = true;
84 | } catch (IOException e) {
85 | MyLog.w(this, MyLog.stackToString(e));
86 | } catch (NullPointerException e) {
87 | // a null pointer exception usually means that the connection is lost
88 | }
89 | }
90 |
91 | try {
92 | MyLog.d(this, "Closing telnet connection");
93 | conn.close();
94 | } catch (IOException e) {
95 | MyLog.w(this, "Error closing connection: " + e.toString());
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/Instrument.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 |
6 | import com.juanvvc.flightgear.BitmapProvider;
7 | import com.juanvvc.flightgear.MyBitmap;
8 | import com.juanvvc.flightgear.PlaneData;
9 |
10 | /** A generic instrument.
11 | * This class manages the list of resources that instruments need, and
12 | * specifically two generic hands, and takes care of scaling images.
13 | * @author juanvi
14 | */
15 | public class Instrument {
16 | /** Position of the instrument in the grid. Unscaled. */
17 | float col;
18 | /** Position of the instrument in the grid. Unscaled. */
19 | float row;
20 | /** Scale all positions with this value! */
21 | float scale;
22 | /** The context of the application. */
23 | Context context;
24 | /** Surfaces of this instrument */
25 | Surface[] surfaces;
26 | /** The grid are squares of gridSize x gridSize */
27 | protected int gridSize = 256;
28 | /** If true, the instrument is ready to be drawn.
29 | * An instrument is ready when its bitmaps are loaded.
30 | */
31 | boolean ready = false;
32 | /** The bitmap cache */
33 | private static BitmapProvider bProvider = null;
34 |
35 | /** Constructor. Call this constructor always from your extended classes!
36 | * @param c The column of the instrument
37 | * @param r The row of the instrument instrument
38 | * @param c A reference to the context of the application
39 | */
40 | public Instrument(float c, float r, Context ctx, Surface... surfaces) {
41 | this.col = c;
42 | this.row = r;
43 | context = ctx;
44 | scale = 1; // we begin unscaled
45 | setSurfaces(surfaces);
46 | ready = false;
47 | }
48 |
49 | public void setSurfaces(Surface[] ss) {
50 | for (Surface s: ss) {
51 | s.setParent(this);
52 | }
53 | surfaces = ss;
54 | }
55 |
56 | public float getCol() {
57 | return col;
58 | }
59 |
60 | public float getRow() {
61 | return row;
62 | }
63 |
64 | public static BitmapProvider getBitmapProvider(Context ctx) {
65 | if (bProvider == null) {
66 | bProvider = new BitmapProvider(ctx);
67 | }
68 | return bProvider;
69 | }
70 |
71 | /** Loads the images in imgFiles.
72 | * @throws Exception If the images cannot be loaded.
73 | */
74 | public void loadImages(String dir) throws Exception {
75 | for(Surface s: this.surfaces) {
76 | // ensures that the manager has loaded the image
77 | MyBitmap b = s.getBitmap();
78 | if (b != null) {
79 | bProvider.getBitmap(dir, b.getFile());
80 | }
81 | }
82 |
83 | if (dir.equals(BitmapProvider.HIGH_QUALITY)) {
84 | this.gridSize = 512;
85 | } else if (dir.equals(BitmapProvider.MEDIUM_QUALITY)) {
86 | this.gridSize = 256;
87 | } else {
88 | this.gridSize = 128;
89 | }
90 |
91 | ready = true;
92 | }
93 |
94 | public float getGridSize() {
95 | return this.gridSize;
96 | }
97 |
98 | /**
99 | * Get the surface of this instrument that controls a screen position.
100 | * In no surface controls this position, returns null.
101 | *
102 | * @param x The screen pixel position of the event
103 | * @param y The screen pixel position of the event
104 | * @return The surfaces that controls that position, or null.
105 | */
106 | public Surface getControlingSurface(float x, float y) {
107 | // transform screen pixels into inner instrument position, in 512 scale
108 | float inX = getXtoInnerX(x);
109 | float inY = getYtoInnerY(y);
110 |
111 | for(Surface s: this.surfaces) {
112 | if (s.youControl(inX, inY)) {
113 | return s;
114 | }
115 | }
116 | return null;
117 | }
118 |
119 |
120 | public Surface[] getSurfaces() {
121 | return this.surfaces;
122 | }
123 |
124 | /**
125 | * @param x The x position of a pixel on the screen
126 | * @return The X position of this x, as an inner, 512 scale point
127 | */
128 | public float getXtoInnerX(float x) {
129 | return Surface.DEFAULT_SURFACE_SIZE * (x / (this.gridSize * this.scale) - this.col);
130 | }
131 |
132 | /**
133 | * @param x The y position of a pixel on the screen
134 | * @return The y position of this x, as an inner, 512 scale point
135 | */
136 | public float getYtoInnerY(float y) {
137 | return Surface.DEFAULT_SURFACE_SIZE * (y / (this.gridSize * this.scale) - this.row);
138 | }
139 |
140 | /** Sets the scale and loads the scaled images into the inner array. */
141 | public void setScale(float scale) {
142 | for (Surface s: surfaces) {
143 | MyBitmap b = s.getBitmap();
144 | if ( b!=null && b.getScaledBitmap() != null) {
145 | b.getScaledBitmap().recycle();
146 | }
147 | }
148 |
149 | this.scale = scale;
150 | // Update all bitmaps
151 | for(Surface s: surfaces) {
152 | MyBitmap b = s.getBitmap();
153 | if (b != null) {
154 | b.updateBitmap(bProvider, this.gridSize);
155 | }
156 | // Inform the surfaces that the bitmap has changed
157 | s.onBitmapChanged();
158 | }
159 | }
160 |
161 | /** @return The scale of the instrument. */
162 | public float getScale() {
163 | return scale;
164 | }
165 |
166 | /** Get a new PlaneData object.
167 | * Some surfaces may call pd.getConnection().get(), that uses the
168 | * network and then it shouldn't be on the main thread.
169 | * @param pd The last PlaneData object */
170 | public void postPlaneData(PlaneData pd) {
171 | for (Surface s: surfaces) {
172 | s.postPlaneData(pd);
173 | }
174 | }
175 |
176 | /** Draw the instrument on the canvas.
177 | *
178 | * @param c The current Canvas
179 | * @param pd The current value of the plane information
180 | */
181 | public void onDraw(Canvas c) {
182 | for (int i = 0; i < surfaces.length; i++) {
183 | Surface s = surfaces[i];
184 | if (s != null) {
185 | // we call onDraw() even if b==null. Maybe the surface is creating its own bitmap
186 | s.onDraw(c);
187 | }
188 | }
189 | }
190 | }
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/InstrumentType.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | public enum InstrumentType {
4 | ALTIMETER,
5 | ATTITUDE,
6 | HEADING,
7 | SPEED,
8 | CLIMB_RATE,
9 | TURN_RATE,
10 | RPM,
11 | RPM2,
12 | OIL_TEMP,
13 | ELECTRIC,
14 | FUEL,
15 | NAV1,
16 | NAV2,
17 | ADF,
18 | BATT,
19 | SWITCHES,
20 | TRIMFLAPS,
21 | HSI1,
22 | BELTS,
23 | COMM1,
24 | COMM2,
25 | COMMADF,
26 | MANIFOLD,
27 | RADAR,
28 | DME,
29 | OIL_PRESS,
30 | CYL_TEMP,
31 | // EGT,
32 | MAGNETS_STARTER,
33 | CLOCK,
34 | ITT,
35 | TORQUE,
36 | PROP,
37 | TURBINE,
38 | FUELFLOW
39 | }
40 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/MagnetosStarterSurface.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import java.io.IOException;
4 |
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 | import android.graphics.Paint;
8 |
9 | import com.juanvvc.flightgear.FGFSConnection;
10 | import com.juanvvc.flightgear.MyBitmap;
11 | import com.juanvvc.flightgear.MyLog;
12 |
13 | /** A surface like a key for the magnetos and started, as the one in may aircrafts. Based on SwitchSurface */
14 | public class MagnetosStarterSurface extends Surface {
15 | private String propMagnetos;
16 | private String propStarter;
17 | private static final int SWITCH_SIZE = 120;
18 | int value; // 0=none, 1=right magnet, 2=left magnet, 3=both, 4=start
19 | private String label;
20 | private float textX, textY;
21 | private Paint textPaint;
22 | private boolean firstRead;
23 | /** ration center */
24 | protected float rcx;
25 | protected float rcy;
26 | // The final position of the surface, scale and gridsize considered (calculated in onBitmapChanged())
27 | private float finalx, finaly;
28 | // The final position of the rotation center, scale and gridsize considered (calculated in onBitmapChanged())
29 | private float finalrx, finalry;
30 | private Matrix m;
31 |
32 | /** If true, the switch needs to be post to the remote fgfs (do not read) */
33 | private boolean dirty = false;
34 |
35 |
36 | public MagnetosStarterSurface(MyBitmap bitmap, final float x, final float y, final float rcX, final float rcY, final String label, final String propMagnetos, final String propStarter) {
37 | super(bitmap, x, y);
38 | this.rcx = rcX;
39 | this.rcy = rcY;
40 | this.label = label;
41 | this.propMagnetos = propMagnetos;
42 | this.propStarter = propStarter;
43 | m = new Matrix();
44 |
45 | if (label != null) {
46 | textPaint = new Paint();
47 | textPaint.setColor(0xffffffff);
48 | }
49 |
50 | firstRead = true;
51 | dirty = false;
52 | }
53 |
54 | @Override
55 | public boolean youControl(final float x, final float y) {
56 | return x > rcx-SWITCH_SIZE && x < rcx+SWITCH_SIZE && y > rcy-SWITCH_SIZE && y < rcy+SWITCH_SIZE;
57 | }
58 |
59 | @Override
60 | public void onMove(final float x, final float y, final boolean end) {
61 | if (end) {
62 | if ( x < DEFAULT_SURFACE_SIZE / 2 && value > 0) {
63 | value -= 1;
64 | dirty = true;
65 | } else if ( x > DEFAULT_SURFACE_SIZE / 2 && value < 4) {
66 | value += 1;
67 | dirty = true;
68 | }
69 | if (value == 4) {
70 | value = 3;
71 | dirty = true;
72 | }
73 | } else {
74 | if (value == 4) {
75 | dirty = true;
76 | } else if ( x > DEFAULT_SURFACE_SIZE / 2 && value == 3) {
77 | value = 4;
78 | dirty = true;
79 | }
80 | }
81 | }
82 |
83 | @Override
84 | public void onBitmapChanged() {
85 | final float realscale = parent.getScale() * parent.getGridSize();
86 | final float col = parent.getCol();
87 | final float row = parent.getRow();
88 | finalx = (col + relx ) * realscale;
89 | finaly = (row + rely ) * realscale;
90 | finalrx = (col + rcx / 512f ) * realscale;
91 | finalry = (row + rcy / 512f ) * realscale;
92 | }
93 |
94 | @Override
95 | public void onDraw(Canvas c) {
96 | if (planeData == null || !planeData.hasData() || bitmap == null || bitmap.getScaledBitmap() == null) {
97 | return;
98 | }
99 |
100 | // draw the label
101 | if (label != null) {
102 | c.drawText(label, textX, textY, textPaint);
103 | }
104 |
105 | float rotation = 0;
106 | switch (value) {
107 | case 0: rotation = -20; break;
108 | case 1: rotation = 10; break;
109 | case 2: rotation = 45; break;
110 | case 3: rotation = 80; break;
111 | case 4: rotation = 120; break;
112 | }
113 |
114 | m.reset();
115 | m.setTranslate(finalx, finaly);
116 | m.postRotate(rotation, finalrx, finalry);
117 | c.drawBitmap(bitmap.getScaledBitmap(), m, null);
118 | }
119 |
120 | @Override
121 | public void postCalibratableSurfaceManager(CalibratableSurfaceManager cs) {
122 | cs.register(this);
123 | }
124 |
125 | @Override
126 | public boolean isDirty() {
127 | return dirty || firstRead;
128 | }
129 |
130 | @Override
131 | public void update(FGFSConnection conn) throws IOException {
132 | if (conn == null || conn.isClosed()) {
133 | return;
134 | }
135 |
136 | if (!dirty || firstRead) {
137 | if (conn.getBoolean(this.propStarter)) {
138 | value = 4;
139 | } else {
140 | value = conn.getInt(this.propMagnetos, 0);
141 | }
142 | // TODO: reading the state from the remote fgfs is SLOW.
143 | // We only read once after creating the switch.
144 | // So, if the user changes the state in the remote fgfs, we will never find out.
145 | firstRead = false;
146 | } else {
147 | // if dirty, post the state of the switch
148 | if (value < 4) {
149 | conn.setInt(this.propMagnetos, value);
150 | conn.setBoolean(this.propStarter, false);
151 | } else {
152 | conn.setInt(this.propMagnetos, 3);
153 | conn.setBoolean(this.propStarter, true);
154 | MyLog.d(this, "Starter");
155 | }
156 | dirty = false;
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/RotateSurface.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Matrix;
5 |
6 | import com.juanvvc.flightgear.MyBitmap;
7 | import com.juanvvc.flightgear.PlaneData;
8 |
9 | /** A surface that is rotated according to some data in PlaneData. */
10 | public class RotateSurface extends Surface {
11 | protected Matrix m;
12 | private float rscale;
13 | /** The property to read from PlaneData, if positive. */
14 | protected int pdIdx;
15 | /** ration center */
16 | protected float rcx;
17 | protected float rcy;
18 | /** min value, and its angle. */
19 | private float min, amin;
20 | /** max value, and its angle. */
21 | private float max, amax;
22 | // The final position of the surface, scale and gridsize considered (calculated in onBitmapChanged())
23 | private float finalx, finaly;
24 | // The final position of the rotation center, scale and gridsize considered (calculated in onBitmapChanged())
25 | private float finalrx, finalry;
26 |
27 | /**
28 | * @param file The file of the image (does not include directory)
29 | * @param x Horizontal position of the surface inside the instrument
30 | * @param y Horizontal position of the surface inside the instrument
31 | * @param pdIdx Index of PlaneData that holds the data
32 | * @param rscale Scale of the data (usually 1: do not modify the data)
33 | * @param rcx Rotation center (inside the instrument)
34 | * @param rcy Rotation center (inside the instrument)
35 | * @param min Minimum value of the data
36 | * @param amin Angle that corresponds to the minimum value
37 | * @param max Max value of the data
38 | * @param amax Angle that corresponds to the max value.
39 | */
40 | public RotateSurface(
41 | MyBitmap bitmap, float x, float y,
42 | int pdIdx, float rscale,
43 | int rcx, int rcy,
44 | float min, float amin, float max, float amax) {
45 | super(bitmap, x, y);
46 | m = new Matrix();
47 | this.pdIdx = pdIdx;
48 | this.rcx = rcx;
49 | this.rcy = rcy;
50 | this.min = min;
51 | this.amin = amin;
52 | this.max = max;
53 | this.amax = amax;
54 | this.rscale = rscale;
55 | }
56 |
57 | protected float getRotationAngle(PlaneData pd) {
58 | float v = pd.getFloat(pdIdx) * rscale;
59 | if (v < min) {
60 | v = min;
61 | } else if (v > max) {
62 | v = max;
63 | }
64 | return (v - min) * (amax - amin) / (max - min) + amin;
65 | }
66 |
67 | @Override
68 | public void onBitmapChanged() {
69 | final float realscale = parent.getScale() * parent.getGridSize();
70 | final float col = parent.getCol();
71 | final float row = parent.getRow();
72 | finalx = (col + relx ) * realscale;
73 | finaly = (row + rely ) * realscale;
74 | finalrx = (col + rcx / 512f ) * realscale;
75 | finalry = (row + rcy / 512f ) * realscale;
76 | }
77 |
78 | @Override
79 | public void onDraw(Canvas c) {
80 | if (planeData == null || !planeData.hasData() || bitmap == null || bitmap.getScaledBitmap() == null) {
81 | return;
82 | }
83 |
84 | m.reset();
85 | m.setTranslate(finalx, finaly);
86 | m.postRotate(getRotationAngle(this.planeData), finalrx, finalry);
87 | c.drawBitmap(bitmap.getScaledBitmap(), m, null);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/SlippingSurface.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import com.juanvvc.flightgear.MyBitmap;
4 |
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 |
8 | /** A surface to control an instrument that moves linearly
9 | * @author juanvi
10 | *
11 | */
12 | public class SlippingSurface extends Surface {
13 | private Matrix m = new Matrix();
14 | private int xmin, xmax;
15 | private int ymin, ymax;
16 | private float min, max;
17 | private float rotation;
18 | private int prop;
19 |
20 | /**
21 | * @param file The image file
22 | * @param rotation The rotation to apply to the image file (it is constant)
23 | * @param prop The index of the property to read
24 | * @param min The minimum value of the property
25 | * @param xmin The X component that corresponds to the minimum value (in a 512 system)
26 | * @param ymin The Y component that corresponds to the minimum value
27 | * @param max
28 | * @param xmax
29 | * @param ymax
30 | */
31 | public SlippingSurface(
32 | MyBitmap bitmap,
33 | float rotation,
34 | int prop,
35 | float min, int xmin, int ymin,
36 | float max, int xmax, int ymax) {
37 | super(bitmap, 0, 0);
38 | this.min = min;
39 | this.xmin = xmin;
40 | this.xmax = xmax;
41 | this.max = max;
42 | this.ymax = ymax;
43 | this.ymin = ymin;
44 | this.rotation = rotation;
45 | this.prop = prop;
46 | }
47 |
48 | @Override
49 | public void onDraw(Canvas c) {
50 | if (planeData == null || !planeData.hasData() || bitmap == null || bitmap.getScaledBitmap() == null) {
51 | return;
52 | }
53 |
54 | float value = planeData.getFloat(this.prop);
55 | relx = ((value - min) * (xmax - xmin) / (max - min) + xmin) / DEFAULT_SURFACE_SIZE;
56 | rely = ((value - min) * (ymax - ymin) / (max - min) + ymin) / DEFAULT_SURFACE_SIZE;
57 |
58 | m.reset();
59 | final float realscale = parent.getScale() * parent.getGridSize();
60 | final float col = parent.getCol();
61 | final float row = parent.getRow();
62 | m.setTranslate(
63 | (col + relx ) * realscale,
64 | (row + rely ) * realscale);
65 | m.postRotate(
66 | this.rotation,
67 | (col + relx ) * realscale,
68 | (row + rely ) * realscale);
69 | c.drawBitmap(bitmap.getScaledBitmap(), m, null);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/StaticSurface.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import com.juanvvc.flightgear.MyBitmap;
4 |
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 |
8 | /** An surfaces that draws an static image on a position. */
9 | public class StaticSurface extends Surface {
10 | Matrix m;
11 | public StaticSurface(MyBitmap bitmap, float x, float y) {
12 | super(bitmap, x, y);
13 | m = null;
14 | }
15 |
16 | @Override
17 | public void onBitmapChanged() {
18 | if (m == null) {
19 | m = new Matrix();
20 | final float gridSize = parent.getGridSize();
21 | final float scale = parent.getScale();
22 | final float col = parent.getCol();
23 | final float row = parent.getRow();
24 | m.setTranslate((col + relx) * gridSize * scale, (row + rely) * gridSize * scale);
25 | }
26 | }
27 |
28 | @Override
29 | public void onDraw(Canvas c) {
30 |
31 | if (planeData == null || !planeData.hasData() || bitmap == null || bitmap.getScaledBitmap() == null || m == null) {
32 | return;
33 | }
34 |
35 | c.drawBitmap(bitmap.getScaledBitmap(), m, null);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/Surface.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import java.io.IOException;
4 |
5 | import android.graphics.Canvas;
6 |
7 | import com.juanvvc.flightgear.FGFSConnection;
8 | import com.juanvvc.flightgear.MyBitmap;
9 | import com.juanvvc.flightgear.PlaneData;
10 |
11 | /** Manages a layer of an instrument.
12 | * A layer of a instrument is usually an image.
13 | * It can be static or respond to some data, such as rotation or so.
14 | * Surfaces are stacked on an instrument.
15 | * @author juanvi
16 | *
17 | */
18 | public abstract class Surface {
19 | /** This is the default surface size, width and height. All positions must be expressed
20 | * relative to this size!
21 | */
22 | public static final float DEFAULT_SURFACE_SIZE = 512f;
23 | /** Horizontal position inside the instrument. 1.0=DEFAULT_SURFACE_FACE pixels */
24 | protected float relx;
25 | /** Vertical position inside the instrument. */
26 | protected float rely;
27 | /** The name of the image file of this surface (does not include the directory) */
28 | protected MyBitmap bitmap;
29 | /** The parent instrument for this surface. */
30 | protected Instrument parent;
31 | /** The last PlaneData object. */
32 | protected PlaneData planeData;
33 |
34 | /**
35 | * @param file The name of the file to load (does not include the directory)
36 | * @param x horizontal position inside the instrument
37 | * @param y vertical position inside the instrument
38 | */
39 | public Surface(MyBitmap bitmap, final float x, final float y) {
40 | this.bitmap = bitmap;
41 | this.relx = x / DEFAULT_SURFACE_SIZE;
42 | this.rely = y / DEFAULT_SURFACE_SIZE;
43 | }
44 |
45 | public void setParent(final Instrument ins) {
46 | this.parent = ins;
47 | }
48 |
49 | /** Returns the parent instrument of this surface */
50 | public Instrument getParent() {
51 | return parent;
52 | }
53 |
54 | /** Returns the bitmap of this surface */
55 | public MyBitmap getBitmap() {
56 | return this.bitmap;
57 | }
58 |
59 | /** The bitmap that the surface uses has changed.
60 | * This usually means that the screen size has changed, or the surface has been loaded.
61 | * The default behavior does nothing, but some surfaces may calculate or update their
62 | * internal reference points, font sizes or transform matrices.
63 | */
64 | public void onBitmapChanged() {
65 | // Does nothing
66 | }
67 |
68 | /** Receives a new planeData.
69 | * Most surfaces don't need to change this method.
70 | * @param pd The last PlaneData object */
71 | public void postPlaneData(final PlaneData pd) {
72 | this.planeData = pd;
73 | }
74 |
75 | /**
76 | * Check if this surface controls a point to change its value.
77 | * If this method returns true, the system will inform the surface abouot any movement
78 | * from this one to the end of the movement. Use to calibrate surfaces on touchable screens.
79 | *
80 | * @param x The x position of an event from the user inside the instrument, in 512 scale
81 | * @param y The y position of an event from the user inside the instrument, in 512 scale
82 | * @return True if this surface controls an event on point (x, y). Currently,
83 | * this method returns always false ("we do not manage movements"). Override to do
84 | * something useful.
85 | */
86 | public boolean youControl(final float x, final float y) {
87 | return false;
88 | }
89 |
90 |
91 | /** Gets a movement event from the user.
92 | * Currently, this method does nothing. To do something useful, override this method
93 | * and return true in youControl(x,y).
94 | * @param x The x position of an event from the user inside the instrument, in 512 scale
95 | * @param y The y position of an event from the user inside the instrument, in 512 scale
96 | * @param end If true, the movement has ended. It is up to the class to detect starting movements.
97 | */
98 | public void onMove(final float x, final float y, final boolean end) {
99 | // Does nothing
100 | }
101 |
102 |
103 | /** Draws the surface.
104 | * @param c The canvas where draw the surface on.
105 | * @param pd The current PlaneData object. */
106 | public abstract void onDraw(final Canvas c);
107 |
108 | /** The CalibratableSurfaceManager changed.
109 | * A CalibratableSurfaceManager is used to control surfaces that have some data to send
110 | * to the remote fgfs: selected radials, switches... A surface that needs send these data,
111 | * will register in the CalibratableSurfaceManager. Then, periodically the manager asks
112 | * if the surface is dirty and, it it is, calls the update method.
113 | * @param cs
114 | */
115 | public void postCalibratableSurfaceManager(final CalibratableSurfaceManager cs) {
116 | // Does nothing
117 | }
118 |
119 | /** Returns true if the surface has some value to send to the remote fgfs */
120 | public boolean isDirty() {
121 | return false;
122 | }
123 |
124 | /** Is it is dirty, this method will be called to send the value to the remote fgfs */
125 | public void update(final FGFSConnection conn) throws IOException {
126 | // Does nothing
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/instruments/SwitchSurface.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.instruments;
2 |
3 | import java.io.IOException;
4 |
5 | import android.graphics.Bitmap;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 | import android.graphics.Rect;
9 |
10 | import com.juanvvc.flightgear.FGFSConnection;
11 | import com.juanvvc.flightgear.MyBitmap;
12 | import com.juanvvc.flightgear.MyLog;
13 |
14 | public class SwitchSurface extends Surface {
15 | private String prop;
16 | private String label;
17 | private boolean state2 = true;
18 | private Paint textPaint;
19 |
20 | private static final int SWITCH_HEIGHT = 152;
21 | private static final int SWITCH_WIDTH = 126;
22 |
23 | private Rect rectOn, rectOff, rectPos;
24 | private float textX, textY;
25 |
26 | private boolean firstRead;
27 |
28 | /** If true, the switch needs to be post to the remote fgfs (do not read) */
29 | private boolean dirty = false;
30 |
31 | public SwitchSurface(MyBitmap bitmap, final float x, final float y, final String prop, final String label) {
32 | super(bitmap, x, y);
33 | this.label = label;
34 | this.prop = prop;
35 | firstRead = true;
36 |
37 | textPaint = new Paint();
38 | textPaint.setColor(0xffffffff);
39 |
40 | MyLog.d(this, "Creating new " + label);
41 | }
42 |
43 | @Override
44 | public boolean youControl(float x, float y) {
45 | return x > relx * DEFAULT_SURFACE_SIZE && x < (relx * DEFAULT_SURFACE_SIZE + SWITCH_WIDTH)
46 | && y > rely * DEFAULT_SURFACE_SIZE && y < (rely * DEFAULT_SURFACE_SIZE + SWITCH_HEIGHT);
47 | }
48 |
49 | @Override
50 | public void onMove(float x, float y, boolean end) {
51 | if (end) {
52 | MyLog.d(this, "Switching " + this.label);
53 | setState(!getState());
54 | }
55 | }
56 |
57 | public void setState(boolean s) {
58 | this.state2 = s;
59 | MyLog.d(this, label + " sets state to " + this.state2);
60 | dirty = true;
61 | }
62 |
63 | public boolean getState() {
64 | return state2;
65 | }
66 |
67 | @Override
68 | public void onBitmapChanged() {
69 | final float realscale = parent.getScale() * parent.getGridSize();
70 | final float scale = parent.getScale();
71 | final float col = parent.getCol();
72 | final float row = parent.getRow();
73 | final int left = (int)((relx + col) * realscale);
74 | final int top = (int)((rely + row) * realscale);
75 | final Bitmap b = this.bitmap.getScaledBitmap();
76 |
77 | rectOn = new Rect(0, 0, b.getWidth(), b.getHeight() / 2);
78 | rectOff = new Rect(0, b.getHeight() / 2, b.getWidth(), b.getHeight());
79 | rectPos = new Rect(left, top, (int)(left + b.getWidth() * scale), (int)(top + b.getHeight() / 2 * scale));
80 | textX = left + b.getWidth() / 5 * scale;
81 | textY = top + 200 * realscale / 512;
82 | }
83 |
84 | @Override
85 | public void onDraw(Canvas c) {
86 | // calculate the position of the switch
87 |
88 |
89 | Bitmap b = this.bitmap.getScaledBitmap();
90 |
91 | // draw the label
92 | c.drawText(label, textX, textY, textPaint);
93 | // draw the switch according to its state
94 | if (getState()) {
95 | c.drawBitmap(b, rectOn, rectPos, null);
96 | } else {
97 | c.drawBitmap(b, rectOff, rectPos, null);
98 | }
99 |
100 | }
101 |
102 | @Override
103 | public void postCalibratableSurfaceManager(CalibratableSurfaceManager cs) {
104 | cs.register(this);
105 | }
106 |
107 | @Override
108 | public boolean isDirty() {
109 | return dirty || firstRead;
110 | }
111 |
112 | @Override
113 | public void update(FGFSConnection conn) throws IOException {
114 | if (conn == null || conn.isClosed()) {
115 | return;
116 | }
117 | // if not moving, just read from the remote connection
118 | if (dirty) {
119 | // if dirty, post the state of the switch
120 | MyLog.i(this, "Updating: " + label + " " + getState());
121 | conn.setBoolean(this.prop, getState());
122 | dirty = false;
123 | } else {
124 | this.setState(conn.getBoolean(this.prop));
125 | // TODO: reading the state from the remote fgfs is SLOW.
126 | // We only read once after creating the switch.
127 | // So, if the user changes the state in the remote fgfs, we will never find out.
128 | firstRead = false;
129 | }
130 | }
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/maps/CompassOverlay.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.maps;
2 |
3 | import org.osmdroid.util.GeoPoint;
4 | import org.osmdroid.views.MapView;
5 | import org.osmdroid.views.MapView.Projection;
6 |
7 | import android.content.Context;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.Matrix;
11 | import android.graphics.Paint;
12 | import android.graphics.Paint.Align;
13 | import android.graphics.Point;
14 |
15 | /** An overlay over a map to draw a compass. */
16 | public class CompassOverlay extends org.osmdroid.views.overlay.Overlay {
17 | private GeoPoint location = null;
18 | private float heading = 0;
19 | private float groundspeed = 0;
20 | private Paint txtPaint = null;
21 | private Paint txt2Paint = null;
22 | private Paint hdgPaint = null;
23 |
24 | public CompassOverlay(Context ctx) {
25 | super(ctx);
26 |
27 | txtPaint = new Paint();
28 | txtPaint.setColor(Color.WHITE);
29 | txtPaint.setTextAlign(Align.CENTER);
30 | txtPaint.setTextSize(16);
31 | txtPaint.setStyle(Paint.Style.STROKE);
32 | txtPaint.setStrokeWidth(2);
33 | txtPaint.setShadowLayer(10, 0, 0, Color.BLACK);
34 |
35 | txt2Paint = new Paint();
36 | txt2Paint.setColor(Color.GRAY);
37 | txt2Paint.setTextAlign(Align.CENTER);
38 | txt2Paint.setStyle(Paint.Style.STROKE);
39 | txt2Paint.setStrokeWidth(6);
40 |
41 | hdgPaint = new Paint();
42 | hdgPaint.setColor(Color.YELLOW);
43 | hdgPaint.setTextAlign(Align.CENTER);
44 | hdgPaint.setTextSize(16);
45 | hdgPaint.setStyle(Paint.Style.STROKE);
46 | hdgPaint.setStrokeWidth(2);
47 | hdgPaint.setShadowLayer(10, 0, 0, Color.BLACK);
48 | }
49 |
50 | /**
51 | * @param l The position of the overlay
52 | * @param h The heading of the overlay
53 | */
54 | public void setPosition(GeoPoint l, float h, float groundspeed) {
55 | location = l;
56 | heading = h;
57 | this.groundspeed = groundspeed;
58 | }
59 |
60 | private static int nmToRadius(float nm, MapView map, double latitude) {
61 | Projection proj = map.getProjection();
62 | float meters = (float) (nm * Projection.METERS_PER_NAUTICAL_MILE);
63 | return (int) (proj.metersToEquatorPixels(meters) * (1 / Math.cos(Math.toRadians(latitude))));
64 | }
65 |
66 | @Override
67 | protected void draw(Canvas pC, MapView mapV, boolean shadow) {
68 | if (shadow || location == null) {
69 | return;
70 | }
71 | Point locPoint = new Point();
72 | final Projection pj = mapV.getProjection();
73 | pj.toMapPixels(location, locPoint);
74 |
75 | // draw circle according to the zoom
76 | int maxradius = 1000; //mapV.getScrollX();
77 | int minradius = 100;
78 | for(int nm: new Integer[]{100, 50, 20, 10, 5, 2, 1}) {
79 | int nmradius = nmToRadius(nm, mapV, location.getLatitude());
80 | if (nmradius < maxradius && nmradius > minradius) {
81 | pC.drawCircle(locPoint.x, locPoint.y, nmradius, txt2Paint);
82 | //pC.drawText(nm +"nm", locPoint.x, locPoint.y - nmradius, txt2Paint);
83 | pC.drawCircle(locPoint.x, locPoint.y, nmradius, txtPaint);
84 | pC.drawText(nm + "nm", locPoint.x, locPoint.y - nmradius, txtPaint);
85 | }
86 | }
87 |
88 | Matrix m = new Matrix();
89 | float[] points = {
90 | 0, nmToRadius(-this.groundspeed * 2 / 60, mapV, location.getLatitude()),
91 | 0, nmToRadius(-this.groundspeed * 5 / 60, mapV, location.getLatitude()),
92 | 0, nmToRadius(-100, mapV, location.getLatitude())};
93 | //m.setTranslate(locPoint.x, locPoint.y);
94 | m.postRotate(this.heading);
95 | m.mapPoints(points);
96 | pC.drawLine(locPoint.x, locPoint.y, locPoint.x + points[4], locPoint.y + points[5], txt2Paint);
97 | pC.drawLine(locPoint.x, locPoint.y, locPoint.x + points[4], locPoint.y + points[5], hdgPaint);
98 | if (this.groundspeed > 40) {
99 | pC.drawText("2", locPoint.x + points[0], locPoint.y + points[1], hdgPaint);
100 | pC.drawText("5", locPoint.x + points[2], locPoint.y + points[3], hdgPaint);
101 | }
102 |
103 |
104 | pC.save();
105 | }
106 | }
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/maps/DatabaseHelper.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.maps;
2 |
3 | import java.io.FileOutputStream;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.OutputStream;
7 |
8 | import android.content.Context;
9 | import android.database.Cursor;
10 | import android.database.SQLException;
11 | import android.database.sqlite.SQLiteDatabase;
12 | import android.database.sqlite.SQLiteException;
13 | import android.database.sqlite.SQLiteOpenHelper;
14 |
15 | public class DatabaseHelper extends SQLiteOpenHelper {
16 | private static String db_path = null;
17 | private static String DB_NAME = "map.db";
18 | private SQLiteDatabase db = null;
19 | private Context context;
20 |
21 | public DatabaseHelper(Context context) {
22 | super(context, DB_NAME, null, 1);
23 | db_path = context.getDatabasePath("map.db").getAbsolutePath();
24 | this.context = context;
25 | }
26 |
27 | /** Creates an empty database in the private directory and copy the database in assets */
28 | public void createDatabase() throws IOException {
29 | if (!checkDatabase()) {
30 | this.getReadableDatabase();
31 | copyDatabase();
32 | }
33 | }
34 |
35 | /** Checks if the database exists in the local filesystem */
36 | private boolean checkDatabase() {
37 | SQLiteDatabase checkDB = null;
38 | try {
39 | checkDB = SQLiteDatabase.openDatabase(db_path, null, SQLiteDatabase.OPEN_READONLY);
40 | } catch(SQLiteException e) {
41 | // database doesn't exist yet
42 | }
43 |
44 | if (checkDB != null) {
45 | checkDB.close();
46 | }
47 |
48 | return (checkDB != null);
49 | }
50 |
51 | private void copyDatabase() throws IOException {
52 | InputStream in = context.getAssets().open(DB_NAME);
53 | OutputStream out = new FileOutputStream(db_path);
54 |
55 |
56 | //transfer bytes from the inputfile to the outputfile
57 | byte[] buffer = new byte[1024];
58 | int length;
59 | while ((length = in.read(buffer))>0){
60 | out.write(buffer, 0, length);
61 | }
62 |
63 | out.flush();
64 | out.close();
65 | in.close();
66 | }
67 |
68 | public void openDatabase() throws SQLException {
69 | this.db = SQLiteDatabase.openDatabase(db_path, null, SQLiteDatabase.OPEN_READONLY);
70 | }
71 |
72 | @Override
73 | public synchronized void close() {
74 | if (this.db != null) {
75 | this.db.close();
76 | }
77 | super.close();
78 | }
79 |
80 | @Override
81 | public void onCreate(SQLiteDatabase db) {
82 | // does nothing, as the database will be copied and not created.
83 | }
84 |
85 | @Override
86 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
87 | // does nothing, as the database will be copied and not created.
88 | }
89 |
90 | public Cursor getAirports(float lat, float lng) {
91 | if (db == null || !db.isOpen()) {
92 | return null;
93 | }
94 | return db.rawQuery(
95 | "select * from airport where lat>? and lat and lng>? and lng",
96 | new String[] {
97 | Float.valueOf(lat-1).toString(), Float.valueOf(lat+1).toString(),
98 | Float.valueOf(lng-1).toString(), Float.valueOf(lng+1).toString()});
99 | }
100 |
101 | public Cursor getNavaids(float lat, float lng) {
102 | if (db == null || !db.isOpen()) {
103 | return null;
104 | }
105 | return db.rawQuery(
106 | "select * from navaid where lat>? and lat and lng>? and lng",
107 | new String[] {
108 | Float.valueOf(lat-1).toString(), Float.valueOf(lat+1).toString(),
109 | Float.valueOf(lng-1).toString(), Float.valueOf(lng+1).toString()});
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/maps/MapOverlay.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.maps;
2 |
3 | import org.osmdroid.util.GeoPoint;
4 | import org.osmdroid.views.MapView;
5 | import org.osmdroid.views.MapView.Projection;
6 |
7 | import android.content.Context;
8 | import android.graphics.Bitmap;
9 | import android.graphics.BitmapFactory;
10 | import android.graphics.Canvas;
11 | import android.graphics.Color;
12 | import android.graphics.Matrix;
13 | import android.graphics.Paint;
14 | import android.graphics.Paint.Align;
15 | import android.graphics.Point;
16 | import android.view.MotionEvent;
17 | import android.widget.Toast;
18 |
19 | import com.juanvvc.flightgear.R;
20 |
21 | /** An overlay over a map to draw a plane. */
22 | public class MapOverlay extends org.osmdroid.views.overlay.Overlay {
23 | private Bitmap bitmap = null;
24 | private GeoPoint location = null;
25 | private float heading = 0;
26 | private String text = null;
27 | private Paint txtPaint = null;
28 | private String description = null;
29 |
30 | public MapOverlay(Context ctx) {
31 | super(ctx);
32 | loadIcon(ctx, R.drawable.plane1);
33 | }
34 |
35 | public void loadIcon(Context ctx, int id) {
36 | bitmap = BitmapFactory.decodeResource(ctx.getResources(),id);
37 | }
38 |
39 | /**
40 | * @param l The position of the overlay
41 | * @param h The heading of the overlay
42 | */
43 | public void setPosition(GeoPoint l, float h) {
44 | location = l;
45 | heading = h;
46 | }
47 |
48 | public void setText(String t) {
49 | txtPaint = new Paint();
50 | txtPaint.setColor(Color.WHITE);
51 | txtPaint.setTextAlign(Align.CENTER);
52 | txtPaint.setTextSize(16);
53 | txtPaint.setShadowLayer(5, 0, 0, Color.BLACK);
54 | text = t;
55 | }
56 |
57 | public void setDescription(String desc) {
58 | description = desc;
59 | }
60 |
61 | @Override
62 | public boolean onDown(MotionEvent e, MapView mapView) {
63 | if (description != null) {
64 | Point locPoint = new Point();
65 | final Projection pj = mapView.getProjection();
66 | pj.toMapPixels(location, locPoint);
67 |
68 | if (e.getX() > locPoint.x - bitmap.getWidth() / 2 && e.getX() < locPoint.x + bitmap.getWidth() / 2 ) {
69 | if (e.getY() > locPoint.y - bitmap.getHeight() / 2 && e.getY() < locPoint.y + bitmap.getHeight() / 2 ) {
70 | Toast.makeText(mapView.getContext(), description, Toast.LENGTH_LONG).show();
71 | return true;
72 | }
73 | }
74 | }
75 | return false;
76 | }
77 |
78 | public static int nmToRadius(float nm, MapView map, double latitude) {
79 | Projection proj = map.getProjection();
80 | float meters = (float) (nm * Projection.METERS_PER_NAUTICAL_MILE);
81 | return (int) (proj.metersToEquatorPixels(meters) * (1 / Math.cos(Math.toRadians(latitude))));
82 | }
83 |
84 | @Override
85 | protected void draw(Canvas pC, MapView mapV, boolean shadow) {
86 | if (shadow || bitmap == null || location == null) {
87 | return;
88 | }
89 | Point locPoint = new Point();
90 | final Projection pj = mapV.getProjection();
91 | pj.toMapPixels(location, locPoint);
92 |
93 | Matrix m = new Matrix();
94 | m.setTranslate(locPoint.x - bitmap.getWidth() / 2, locPoint.y - bitmap.getHeight() / 2);
95 | m.postRotate(heading, locPoint.x, locPoint.y);
96 |
97 | pC.drawBitmap(bitmap, m, txtPaint);
98 | if (text != null) {
99 | pC.drawText(text, locPoint.x, locPoint.y, txtPaint);
100 | }
101 | pC.save();
102 | }
103 | }
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/maps/SolidTileSource.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.maps;
2 |
3 | import java.io.InputStream;
4 |
5 | import org.osmdroid.ResourceProxy;
6 | import org.osmdroid.tileprovider.MapTile;
7 | import org.osmdroid.tileprovider.tilesource.BitmapTileSourceBase.LowMemoryException;
8 | import org.osmdroid.tileprovider.tilesource.ITileSource;
9 |
10 | import com.juanvvc.flightgear.MyLog;
11 | import com.juanvvc.flightgear.R;
12 |
13 | import android.content.Context;
14 | import android.graphics.drawable.Drawable;
15 |
16 | public class SolidTileSource implements ITileSource {
17 | private Context context;
18 |
19 | public SolidTileSource(Context c) {
20 | this.context = c;
21 | }
22 |
23 | @Override
24 | public Drawable getDrawable(String arg0) throws LowMemoryException {
25 | MyLog.i(this, "Loading from file");
26 | return context.getResources().getDrawable(R.drawable.solidtile);
27 | }
28 |
29 | @Override
30 | public Drawable getDrawable(InputStream arg0) throws LowMemoryException {
31 | MyLog.i(this, "Loading from stream");
32 | return context.getResources().getDrawable(R.drawable.solidtile);
33 | }
34 |
35 | @Override
36 | public int getMaximumZoomLevel() {
37 | return 10;
38 | }
39 |
40 | @Override
41 | public int getMinimumZoomLevel() {
42 | return 2;
43 | }
44 |
45 | @Override
46 | public String getTileRelativeFilenameString(MapTile arg0) {
47 | MyLog.i(this, "Loading from tile");
48 | return "";
49 | }
50 |
51 | @Override
52 | public int getTileSizePixels() {
53 | return 256;
54 | }
55 |
56 | @Override
57 | public String localizedName(ResourceProxy arg0) {
58 | return "SolidTileSource";
59 | }
60 |
61 | @Override
62 | public String name() {
63 | return "SolidTileSource";
64 | }
65 |
66 | @Override
67 | public int ordinal() {
68 | return 1;
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/panels/Cessna337.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.panels;
2 |
3 | import java.util.ArrayList;
4 |
5 | import android.content.Context;
6 |
7 | import com.juanvvc.flightgear.MyBitmap;
8 | import com.juanvvc.flightgear.MyLog;
9 | import com.juanvvc.flightgear.PlaneData;
10 | import com.juanvvc.flightgear.instruments.CalibratableRotateSurface;
11 | import com.juanvvc.flightgear.instruments.Instrument;
12 | import com.juanvvc.flightgear.instruments.InstrumentType;
13 | import com.juanvvc.flightgear.instruments.RotateSurface;
14 | import com.juanvvc.flightgear.instruments.StaticSurface;
15 | import com.juanvvc.flightgear.instruments.Surface;
16 | import com.juanvvc.flightgear.instruments.SwitchSurface;
17 |
18 | /** Distribute instruments as in a Cessna 337 Skymaster */
19 | public class Cessna337 {
20 |
21 | public static Instrument createInstrument(InstrumentType type, Context context, float col, float row) {
22 | MyBitmap hand1 = new MyBitmap("misc1.png", 380, 10, 40, 270); // used in the ASI (long hand)
23 | MyBitmap hand3 = new MyBitmap("misc2.png", 4, 200, 140, 24); // used in small instruments
24 | MyBitmap hand4 = new MyBitmap("misc2.png", 40, 200, 100, 24); // used in even smaller instruments
25 | MyBitmap headings = new MyBitmap("nav2.png", -1, -1, -1, -1);
26 | MyBitmap switches1 = new MyBitmap("switches.png", 0, 0, 128, 368);
27 | MyBitmap switches2 = new MyBitmap("switches.png", 128, 0, 128, 368);
28 | MyBitmap switches3 = new MyBitmap("switches.png", 258, 0, 122, 306);
29 |
30 |
31 | switch (type) {
32 | case SPEED:
33 | return new Instrument(col, row, context, new Surface[] {
34 | new StaticSurface(new MyBitmap("speed2.png", -1, -1, -1, -1), 0, 0),
35 | new RotateSurface(hand1, 236, 56, PlaneData.SPEED, 1, 256, 256, 0, 0, 200, 350),
36 | new CalibratableRotateSurface(new MyBitmap("speed4.png", -1, -1, -1, -1), 256-228, 256-228, null, true, -1, 256, 256, 0, 0, 360, -360),
37 | });
38 | case RPM:
39 | return new Instrument(col, row, context, new Surface[] {
40 | new StaticSurface(new MyBitmap("rpm2.png", -1, -1, -1, -1), 0, 0),
41 | new RotateSurface(hand1, 276-20, 56, PlaneData.RPM, 1, 276, 256, 500, 270-60, 3000, 270+60),
42 | new RotateSurface(hand1, 236-20, 56, PlaneData.RPM2, 1, 236, 256, 500, 90+60, 3000, 90-60),
43 | new StaticSurface(new MyBitmap("misc4.png", 160, 0, 160, 160), 256-80, 256-80)
44 | });
45 | case MANIFOLD:
46 | return new Instrument(col, row, context, new Surface[] {
47 | new StaticSurface(new MyBitmap("manifold2.png", -1, -1, -1, -1), 0, 0),
48 | new RotateSurface(hand3, 276, 244, PlaneData.MANIFOLD, 1, 256, 256, 10, 180-70, 32, 180+60),
49 | new RotateSurface(hand3, 276, 244, PlaneData.MANIFOLD2, 1, 256, 256, 10, 70, 32, -60),
50 | new StaticSurface(new MyBitmap("misc4.png", 0, 0, 160, 160), 256-80, 256-80)
51 | });
52 | case HEADING: // Actually, it is a RMI
53 | return new Instrument(col, row, context, new Surface[] {
54 | new StaticSurface(new MyBitmap("nav3.png", 0, 190, 320, 320), 256-160, 256-160),
55 | new RotateSurface(new MyBitmap("nav4.png", 398, 50, 54, 324), 256-27, 256-162, PlaneData.ADF_DEFLECTION, 1, 256, 256, 0, 0, 720, 720), // for some reason, the ADF instrument shows headings from 0 to 720
56 | new RotateSurface(headings, 0, 0, PlaneData.HEADING, 1, 256, 256, 0, 0, 360, -360),
57 | new C172HIBug(new MyBitmap("misc2.png", 242, 290, 44, 44), 256-22, 36, "/autopilot/settings/heading-bug-deg", true, -1, 256, 256, -180, -180, 180, 180),
58 | new C172HIBug(new MyBitmap("nav4.png", 248, 200, 32, 300), 236, 100, null, false, PlaneData.NAV1_HEADING, 256, 256, 0, 0, 360, 360),
59 | new StaticSurface(new MyBitmap("nav5.png", -1, -1, -1, -1), 0, 0),
60 | new StaticSurface(new MyBitmap("nav1.png", -1, -1, -1, -1), 0, 0)
61 | });
62 | case RADAR:
63 | return new Instrument(col, row, context, new Surface[] {
64 | new StaticSurface(new MyBitmap("radar1.png", -1, -1, -1, -1), 0, 0),
65 | new CalibratableRotateSurface(new MyBitmap("misc2.png", 242, 290, 44, 44), 256-22, 12, null, false, -1, 256, 256, 0, 0, 240, 240),
66 | new C337RadarSurface(hand1, 236, 56, PlaneData.ALTITUDE_AGL, 1, 256, 256, 0, 0, 2500, 240),
67 | new StaticSurface(new MyBitmap("radar2.png", -1, -1, -1, -1), 0, 0)
68 | });
69 | case FUEL:
70 | return new Instrument(col, row, context, new Surface[] {
71 | new StaticSurface(new MyBitmap("Fuel-Oil-base.png", 0, 0, 512, 204), 0, 0),
72 | new RotateSurface(hand4, 154, 142, PlaneData.FUEL1, 1, 154, 154, 0, -150, 75, -30),
73 | new RotateSurface(hand4, 400, 142, PlaneData.FUEL2, 1, 400, 154, 0, -150, 75, -30)
74 | });
75 | case OIL_PRESS:
76 | return new Instrument(col, row, context, new Surface[] {
77 | new StaticSurface(new MyBitmap("Fuel-Oil-base.png", 0, 212, 512, 204), 0, 0),
78 | new RotateSurface(hand4, 120, 142, PlaneData.OIL_PRESS, 1, 120, 154, 0, -145, 100, -35),
79 | new RotateSurface(hand4, 368, 142, PlaneData.OIL2_PRESS, 1, 368, 154, 0, -145, 100, -35)
80 | });
81 | case CYL_TEMP:
82 | return new Instrument(col, row, context, new Surface[] {
83 | new StaticSurface(new MyBitmap("CHT-Oil-base.png", 0, 0, 512, 204), 0, 0),
84 | new RotateSurface(hand4, 154, 142, PlaneData.CHT1_TEMP, 1, 154, 154, 200, -145, 500, -35),
85 | new RotateSurface(hand4, 400, 142, PlaneData.CHT2_TEMP, 1, 400, 154, 200, -145, 500, -35)
86 | });
87 | case OIL_TEMP:
88 | return new Instrument(col, row, context, new Surface[] {
89 | new StaticSurface(new MyBitmap("CHT-Oil-base.png", 0, 212, 512, 204), 0, 0),
90 | new RotateSurface(hand4, 120, 142, PlaneData.OIL_TEMP, 1, 120, 154, 75, -145, 260, -35),
91 | new RotateSurface(hand4, 368, 142, PlaneData.OIL2_TEMP, 1, 368, 154, 75, -145, 260, -35),
92 |
93 | });
94 | case SWITCHES:
95 | return new Instrument(col, row, context, new Surface[] {
96 | new SwitchSurface(switches2, 0, 0, "/controls/engines/engine[0]/master-bat", "BATT"),
97 | new SwitchSurface(switches2, 128, 0, "/controls/engines/engine[0]/master-alt", "ALT1"),
98 | new SwitchSurface(switches2, 256, 0, "/controls/engines/engine[1]/master-alt", "ALT2"),
99 | new SwitchSurface(switches1, 384, 0, "/controls/switches/master-avionics", "AVI"),
100 |
101 |
102 | new SwitchSurface(switches3, 512+0, 0, "/controls/lighting/nav-lights", "NAV"),
103 | new SwitchSurface(switches3, 512+128, 0, "/controls/lighting/beacon", "BCN"),
104 | new SwitchSurface(switches3, 512+256, 0, "/controls/lighting/taxi-light", "TAX"),
105 | new SwitchSurface(switches3, 512+384, 0, "/controls/lighting/landing-lights", "LNG"),
106 | });
107 |
108 | default:
109 | MyLog.w(Cessna337.class.getSimpleName(), "Instrument not available: " + type);
110 | return null;
111 | }
112 | }
113 |
114 | public static ArrayList getInstrumentPanel(Context context) {
115 | final ArrayList instruments = new ArrayList();
116 | instruments.add(Cessna337.createInstrument(InstrumentType.SPEED, context, 1, 0));
117 | instruments.add(Cessna172.createInstrument(InstrumentType.ATTITUDE, context, 2, 0));
118 | instruments.add(Cessna172.createInstrument(InstrumentType.ALTIMETER, context, 3, 0));
119 | instruments.add(Cessna172.createInstrument(InstrumentType.NAV1, context, 4, 0));
120 | instruments.add(Cessna172.createInstrument(InstrumentType.TURN_RATE, context, 1, 1));
121 | instruments.add(Cessna337.createInstrument(InstrumentType.HEADING, context, 2, 1));
122 | instruments.add(Cessna172.createInstrument(InstrumentType.CLIMB_RATE, context, 3, 1));
123 | instruments.add(Cessna172.createInstrument(InstrumentType.NAV2, context, 4, 1));
124 |
125 | instruments.add(Cessna337.createInstrument(InstrumentType.MANIFOLD, context, 0, 0));
126 | instruments.add(Cessna337.createInstrument(InstrumentType.RPM, context, 0, 1));
127 | instruments.add(Cessna337.createInstrument(InstrumentType.RADAR, context, 4, 2));
128 |
129 | instruments.add(Cessna337.createInstrument(InstrumentType.FUEL, context, 0, 2));
130 | instruments.add(Cessna337.createInstrument(InstrumentType.OIL_PRESS, context, 1, 2));
131 | instruments.add(Cessna337.createInstrument(InstrumentType.CYL_TEMP, context, 0, 2.5f));
132 | instruments.add(Cessna337.createInstrument(InstrumentType.OIL_TEMP, context, 1, 2.5f));
133 |
134 | instruments.add(Cessna337.createInstrument(InstrumentType.SWITCHES, context, 2, 2));
135 | instruments.add(Cessna172.createInstrument(InstrumentType.TRIMFLAPS, context, 3, 2.5f));
136 | instruments.add(Cessna172.createInstrument(InstrumentType.DME, context, 2, 2.5f));
137 | return instruments;
138 | }
139 | }
140 |
141 | /** The radar altitude instrument that I chose is not linear.
142 | * This surface rotates the handle according to some predefined values.
143 | */
144 | class C337RadarSurface extends RotateSurface {
145 |
146 | public C337RadarSurface(MyBitmap bitmap,
147 | float x, float y,
148 | int pdIdx, float rscale,
149 | int rcx, int rcy,
150 | float min, float amin, float max, float amax) {
151 | super(bitmap, x, y, pdIdx, rscale, rcx, rcy, min, amin, max, amax);
152 | }
153 |
154 | @Override
155 | protected float getRotationAngle(PlaneData pd) {
156 | float v = pd.getFloat(pdIdx);
157 | if (v < 500) { // from 0 to 500, angles from 0 to 160
158 | return 160.0f * v/500;
159 | } else if(v < 1000) { // from 500 to 1500, angles from 160 to 180.
160 | return 160 + 20f * (v - 500) / 500;
161 | } else if(v < 1500) { // from 1000 to 1500, angles from 180 to 200
162 | return 180 + 20f * (v - 1000) / 500;
163 | } else if(v < 2000) { // from 1500 to 2000, angles from 200 to 220
164 | return 200 + 20f * (v - 1500) / 500;
165 | } else if(v < 2500) { // from 2000 to 2500, angles from 220 to 240
166 | return 220 + 20f * (v - 2000) / 500;
167 | } else { // other values: saturated
168 | return 240;
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/panels/LiquidDisplay.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.panels;
2 |
3 | import java.util.ArrayList;
4 |
5 | import android.content.Context;
6 | import android.graphics.Bitmap;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.LinearGradient;
10 | import android.graphics.Matrix;
11 | import android.graphics.Paint;
12 | import android.graphics.Region;
13 | import android.graphics.Shader;
14 | import android.graphics.Typeface;
15 |
16 | import com.juanvvc.flightgear.MyBitmap;
17 | import com.juanvvc.flightgear.MyLog;
18 | import com.juanvvc.flightgear.PlaneData;
19 | import com.juanvvc.flightgear.instruments.Instrument;
20 | import com.juanvvc.flightgear.instruments.InstrumentType;
21 | import com.juanvvc.flightgear.instruments.RotateSurface;
22 | import com.juanvvc.flightgear.instruments.StaticSurface;
23 | import com.juanvvc.flightgear.instruments.Surface;
24 |
25 | /** Distributes instruments in a liquid display.
26 | * TODO: this class is not working (and probably never will) */
27 | public class LiquidDisplay {
28 | public static Instrument createInstrument(InstrumentType type, Context context, float col, float row) {
29 | switch(type) {
30 | case ATTITUDE:
31 | return new Instrument(col, row, context, new Surface[] {
32 | new LiquidAtiSurface(new MyBitmap("pitchscale.png", -1, -1, -1, -1), 70, 138),
33 | new RotateSurface(new MyBitmap("ai.roll.ref.png", -1, -1, -1, -1), 0, 0, PlaneData.ROLL, 1, 256, 256, -180, 180, 180, -180),
34 | new StaticSurface(new MyBitmap("ai.ref.png", -1, -1, -1, -1), -256, -162)
35 | });
36 | // case HSI1:
37 | // // The center of the instrument is (256, 274)
38 | // return new Instrument(col, row, context, new Surface[] {
39 | // new CalibratableRotateSurface(new MyBitmap("liquid.hsi3.png", 0, 0, 328, 328), 256-164, 274-164, "/instrumentation/heading-indicator/indicated-heading-deg", true, PlaneData.HEADING, 256, 274, 0, 0, 360, -360),
40 | // new StaticSurface(new MyBitmap("liquid.hsi2.png", 0, 0, 408, 416), 256-204, 256-208),
41 | // new SlippingSurface(new MyBitmap("liquid.hsi2.png", 412, 124, 32, 32), 0, PlaneData.GS1_DEFLECTION, -1, 50, 256+85, 1, 50, 256-85),
42 | // new SlippingSurface(new MyBitmap("liquid.hsi2.png", 452, 124, 32, 32), 0, PlaneData.GS1_DEFLECTION, -1, 430, 256+85, 1, 430, 256-85),
43 | // new HSINeedle(new MyBitmap("liquid.hsi2.png", 444, 164, 32, 64), 256-16, 340, PlaneData.NAV1_SEL_RADIAL, PlaneData.HEADING, 180), // CDI, head
44 | // new HSINeedle(new MyBitmap("liquid.hsi2.png", 484, 172, 20, 68), 256-10, 130, PlaneData.NAV1_SEL_RADIAL, PlaneData.HEADING, 180), // CDI, tail
45 | // new HSINeedle(new MyBitmap("liquid.hsi2.png", 178, 456, 184, 52), 256-92, 274-26, PlaneData.NAV1_SEL_RADIAL, PlaneData.HEADING, 0), // CDI, scale
46 | // new HSINeedleDeflection(new MyBitmap("liquid.hsi2.png", 412, 172, 16, 152), 256-8, 274-76, PlaneData.NAV1_SEL_RADIAL, PlaneData.HEADING, PlaneData.NAV1_DEFLECTION, 0), // CDI, deflection
47 | // new StaticSurface(new MyBitmap("liquid.hsi1.png", -1, -1, -1, -1), 0, 0)
48 | // });
49 | case BELTS:
50 | Typeface face = Typeface.createFromAsset(context.getAssets(), "14_LED1.ttf");
51 | return new Instrument(col, row, context, new Surface[] {
52 | new AltitudeBeltSurface(128, 512, face),
53 | new SpeedBeltSurface(128, 512, face)
54 | });
55 | default:
56 | MyLog.w(LiquidDisplay.class, "Instrument is null: " + type);
57 | return null;
58 | }
59 | }
60 |
61 | public static ArrayList getInstrumentPanel(Context context) {
62 | final ArrayList instruments = new ArrayList();
63 | instruments.add(createInstrument(InstrumentType.ATTITUDE, context, 0f, 0.0f));
64 | instruments.add(createInstrument(InstrumentType.HSI1, context, 0f, 1.0f));
65 | instruments.add(createInstrument(InstrumentType.BELTS, context, 0, 0.15f));
66 | return instruments;
67 | }
68 | }
69 |
70 | class LiquidAtiSurface extends Surface {
71 | private Matrix matrix;
72 | private Paint paintGrad = null;
73 | private Paint shader = null;
74 |
75 | public LiquidAtiSurface(MyBitmap bitmap, float x, float y) {
76 | super(bitmap, x, y);
77 | matrix = new Matrix();
78 | paintGrad = new Paint();
79 | shader = new Paint();
80 | }
81 | @Override
82 | public void onDraw(Canvas c) {
83 | if (planeData == null) {
84 | return;
85 | }
86 |
87 | Bitmap b = bitmap.getScaledBitmap();
88 |
89 | // calculate pitch and matrix
90 | matrix.reset();
91 | float col = parent.getCol();
92 | float row = parent.getRow();
93 | float gridSize = parent.getGridSize();
94 | float scale = parent.getScale();
95 | // translate 25 / pixels each 5 degrees
96 | float roll = planeData.getFloat(PlaneData.ROLL);
97 | if (roll > 60) {
98 | roll = 60;
99 | }
100 | float pitch = planeData.getFloat(PlaneData.PITCH);
101 | if (pitch > 45) {
102 | pitch = 45;
103 | }
104 | matrix.postTranslate(((0.5f + col) * gridSize) * scale - b.getWidth() / 2, ((0.5f + row) * gridSize + pitch * (25 * gridSize/ 512) / 5) * scale - b.getHeight() / 2);
105 | matrix.postRotate(-roll, ((0.5f + col) * gridSize) * scale, ((0.5f + row) * gridSize) * scale);
106 |
107 | // draw background
108 | float[] p = {0, 0, 0, b.getHeight()};
109 | matrix.mapPoints(p);
110 | paintGrad.setShader(new LinearGradient(
111 | p[0], p[1], p[2], p[3],
112 | new int[]{0xff0000aa, 0xff0000ff, 0xff0000ff, 0xffffffff, 0xffffffff, 0xffA36008, 0xffA36008},
113 | new float[]{0f, 0.25f, 0.49f, 0.495f, 0.505f, 0.51f, 1.0f},
114 | Shader.TileMode.CLAMP));
115 | c.drawPaint(paintGrad);
116 |
117 | // draw scale
118 | shader.setShader(new LinearGradient(
119 | ((col + 0.5f) * gridSize) * scale, (row * gridSize) * scale,
120 | ((col + 0.5f) * gridSize) * scale, ((1f + row) * gridSize) * scale,
121 | new int[]{0x0fff, 0xffff, 0xffff, 0x0fff}, null,
122 | Shader.TileMode.CLAMP));
123 | // shader.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.SRC_IN));
124 | c.drawBitmap(b, matrix, shader);
125 | }
126 | }
127 |
128 | abstract class NumberBeltSurface extends Surface {
129 | private int height, width;
130 | private int interval1;
131 | private boolean negative;
132 | private int size;
133 | private Typeface face;
134 |
135 | public NumberBeltSurface(float x, float y, int size, int interval1, boolean negative, int width, int height, Typeface face) {
136 | super(null, x, y);
137 | this.interval1 = interval1;
138 | this.size = size;
139 | this.negative = negative;
140 | this.width = width;
141 | this.height = height;
142 | this.face = face;
143 |
144 | MyLog.d(this, "NumberBeltInitialized");
145 | }
146 |
147 | public void onDraw(Canvas c, float value) {
148 | int j0 = (int) Math.ceil(value / interval1 - size / (2.0 * interval1));
149 | if (!negative) {
150 | j0 = Math.max(0, j0);
151 | }
152 | int j1 = (int) Math.floor(value / interval1 + size / (2.0 * interval1));
153 |
154 | float realscale = parent.getScale() * parent.getGridSize();
155 | final float col = parent.getCol();
156 | final float row = parent.getRow();
157 |
158 | // The background
159 | Paint grey = new Paint();
160 | grey.setColor(0xee666666);
161 | grey.setStyle(Paint.Style.FILL);
162 | float x0 = (col + relx) * realscale;
163 | float y0 = (row + rely) * realscale;
164 | c.clipRect(x0, y0, x0 + (width / 512f) * realscale, y0 + (height / 512f) * realscale, Region.Op.REPLACE);
165 | c.drawPaint(grey);
166 |
167 | // the scale
168 | Paint font = new Paint();
169 | font.setColor(Color.WHITE);
170 | font.setTextSize(20);
171 | font.setTypeface(this.face);
172 | for (int j=j0; j<=j1; j++) {
173 | float oy = (height / 512f) * (1.0f - (1.0f * j * interval1 - value) / size - 0.5f) * realscale;
174 | c.drawText((Integer.valueOf(j*interval1).toString()), x0, y0 + oy, font);
175 | }
176 |
177 | // the actual value and its background
178 | font.setColor(Color.WHITE);
179 | font.setTextSize(40);
180 | c.clipRect(x0, y0 + 0.4f * (height / 512f) * realscale, x0 + (width / 512f) * realscale, y0 + 0.6f * (height / 512f) * realscale);
181 | Paint black = new Paint();
182 | black.setColor(Color.BLACK);
183 | black.setStyle(Paint.Style.FILL);
184 | c.drawPaint(black);
185 | c.drawText(Float.valueOf(value).toString(), x0, y0 + (height / (2 * 512f)) * realscale, font);
186 |
187 | //c.clipRect(0, 0, c.getWidth(), c.getHeight(), Region.Op.REPLACE);
188 | }
189 | }
190 |
191 | class AltitudeBeltSurface extends NumberBeltSurface {
192 | public AltitudeBeltSurface(int width, int height, Typeface face) {
193 | super(512 - width, 0, 1000, 200, true, width, height, face);
194 | }
195 |
196 | @Override
197 | public void onDraw(Canvas c) {
198 | if (planeData == null) {
199 | return;
200 | }
201 | super.onDraw(c, (int) Math.floor(planeData.getFloat(PlaneData.ALTITUDE)));
202 | }
203 | }
204 | class SpeedBeltSurface extends NumberBeltSurface {
205 | public SpeedBeltSurface(int width, int height, Typeface face) {
206 | super(0, 0, 40, 10, false, width, height, face);
207 | }
208 |
209 | @Override
210 | public void onDraw(Canvas c) {
211 | if (planeData == null) {
212 | return;
213 | }
214 | super.onDraw(c, (int) Math.floor(planeData.getFloat(PlaneData.SPEED)));
215 | }
216 | }
--------------------------------------------------------------------------------
/FlightGearMap/src/com/juanvvc/flightgear/panels/SenecaII.java:
--------------------------------------------------------------------------------
1 | package com.juanvvc.flightgear.panels;
2 |
3 | import java.util.ArrayList;
4 |
5 | import android.content.Context;
6 |
7 | import com.juanvvc.flightgear.instruments.Instrument;
8 | import com.juanvvc.flightgear.instruments.InstrumentType;
9 |
10 | public class SenecaII {
11 |
12 | public static ArrayList getInstrumentPanel(Context context) {
13 | final ArrayList instruments = new ArrayList();
14 | instruments.add(Cessna337.createInstrument(InstrumentType.SPEED, context, 1, 0));
15 | instruments.add(Cessna172.createInstrument(InstrumentType.ATTITUDE, context, 2, 0));
16 | instruments.add(Cessna172.createInstrument(InstrumentType.ALTIMETER, context, 3, 0));
17 | instruments.add(Cessna337.createInstrument(InstrumentType.RADAR, context, 4, 0));
18 | instruments.add(Cessna172.createInstrument(InstrumentType.ADF, context, 0, 1));
19 | instruments.add(Cessna172.createInstrument(InstrumentType.TURN_RATE, context, 1, 1));
20 | instruments.add(Cessna172.createInstrument(InstrumentType.HSI1, context, 2, 1));
21 | instruments.add(Cessna172.createInstrument(InstrumentType.CLIMB_RATE, context, 3, 1));
22 | instruments.add(Cessna172.createInstrument(InstrumentType.NAV2, context, 4, 1));
23 |
24 | instruments.add(Cessna337.createInstrument(InstrumentType.MANIFOLD, context, 3, 2.5f));
25 | instruments.add(Cessna172.createInstrument(InstrumentType.RPM, context, 2, 2.5f));
26 | instruments.add(Cessna172.createInstrument(InstrumentType.RPM2, context, 4, 2.5f));
27 |
28 | instruments.add(Cessna337.createInstrument(InstrumentType.FUEL, context, 0, 2));
29 | instruments.add(Cessna337.createInstrument(InstrumentType.OIL_PRESS, context, 1, 2));
30 | instruments.add(Cessna337.createInstrument(InstrumentType.CYL_TEMP, context, 3, 2));
31 | instruments.add(Cessna337.createInstrument(InstrumentType.OIL_TEMP, context, 4, 2));
32 |
33 | instruments.add(Cessna337.createInstrument(InstrumentType.SWITCHES, context, 0, 2.5f));
34 | instruments.add(Cessna172.createInstrument(InstrumentType.TRIMFLAPS, context, 0, 0));
35 | instruments.add(Cessna172.createInstrument(InstrumentType.DME, context, 2, 2));
36 | return instruments;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 |
2 | This is an Atlas-like application for Flightgear.
3 | This application does not run on its own: you need FlightGear running in your
4 | system.
5 |
6 | = Installation =
7 |
8 | Connect your Android device to the local Wifi network and run the
9 | application. A dialog should appear with the IP of your device.
10 |
11 | In your PC, copy the archive andatlas.xml to the directory FG_ROOT/Protocol
12 |
13 | Run FlightGear with this option:
14 |
15 | --generic=socket,out,5,the-ip-of-your-android-device,5501,udp,andatlas
16 |
17 | where the-ip-of-your-android-device is the IP shown by your Android device.
18 |
19 | If you find any problem, close the android application and run again.
20 | Usually, you do not need to restart FlightGear.
21 |
22 | I'll be available as "ludomotico" at the forums of FlightGear, www.flightgear.org/forums,
23 | section Support->Atlas.
24 |
25 | Enjoy.
26 |
27 | = Legal =
28 |
29 | This application is under the GPLv3 license.
30 | Check: http://www.gnu.org/licenses/gpl-3.0.html
31 |
32 | (c) 2012, Juan Vera del Campo, juanvvc@gmail.com
33 | Graphics: (c) 2012, FlightGear.com
34 | Maps: (c) 2012 Google co.
35 |
--------------------------------------------------------------------------------
/andatlas-b1900d.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
310 |
311 |
312 |
313 |
314 |
--------------------------------------------------------------------------------
/andatlas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
310 |
311 |
312 |
313 |
314 |
--------------------------------------------------------------------------------
/assets.high.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Juanvvc/FlightGearMap/c69e3403dccdbc2d1b599893aed11a766a3580b2/assets.high.zip
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Juanvvc/FlightGearMap/c69e3403dccdbc2d1b599893aed11a766a3580b2/logo.png
--------------------------------------------------------------------------------
/nav.xcf.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Juanvvc/FlightGearMap/c69e3403dccdbc2d1b599893aed11a766a3580b2/nav.xcf.gz
--------------------------------------------------------------------------------
/switches.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Juanvvc/FlightGearMap/c69e3403dccdbc2d1b599893aed11a766a3580b2/switches.blend
--------------------------------------------------------------------------------
/switches.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Juanvvc/FlightGearMap/c69e3403dccdbc2d1b599893aed11a766a3580b2/switches.png
--------------------------------------------------------------------------------
/trimflaps.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
251 |
--------------------------------------------------------------------------------