I tried to find how to get the altitude above the mean sea level.
At this time, it returns altitude from the ellipsoid.
So, anyone knows the formula or calculation to change the altitude value
from ellipsoid to the altitude value from mean sea level.
Thank you for all help
As you mentioned, GPS returns the altitude as an offset from the WGS84 reference ellipsoid, but most people want to see mean sea level (MSL), and the two frequently don't agree. The way this is most frequently done is by looking up the delta in a table and using that to compute MSL based on the height from GPS and the delta in the table.
There's some java code here: https://github.com/NASAWorldWind/WorldWindJava/blob/develop/src/gov/nasa/worldwind/util/EGM96.java. The other functions that it uses from Worldwind aren't that complicated, so you could probably use most of the code unmodified, and the rest you could adapt if you're working in Java and their license meets your needs.
It uses information from the EGM 96 data set (link here if you're interested -- not strictly necessary though), which you can download here: https://github.com/jleppert/egm96/blob/master/WW15MGH.DAC. You will want the WW15MGH.DAC file. It's a binary file full of 16-bit signed integers. You can use the Java example to show you how to access the data in the file. They also provide a Fortran example if that's your thing. :-)
Here's the information on the file from their readme.
Data Description for 15 minute worldwide binary geoid height file:
---- FILE: WW15MGH.DAC
The total size of the file is 2,076,480 bytes. This file was created
using an INTEGER2 data type format and is an unformatted direct access
file. The data on the file is arranged in records from north to south.
There are 721 records on the file starting with record 1 at 90 N. The
last record on the file is at latitude 90 S. For each record, there
are 1,440 15 arc-minute geoid heights arranged by longitude from west to
east starting at the Prime Meridian (0 E) and ending 15 arc-minutes west
of the Prime Meridian (359.75 E). On file, the geoid heights are in units
of centimeters. While retrieving the Integer2 values on file, divide by
100 and this will produce a geoid height in meters.
Related
I want to know what is the appropriate process of converting Amplitude to dB. I am using double as below
db = (20 * Math.log10(mediaRecorder.getMaximimAmplitude));
But there are suggestions to use double as below
db = (20 * Math.log10(x2 / REFERENCE));
I dont know what reference is to use in which scenerio
The decibel is a much misused unit. It is defined as the 10 log (P1/P2) where P1 is the measured power, and P2 is the reference power. That is, it is always relative to some reference power. A common reference power is one milliwatt, and this is the definition of dBm. 0dBm is one milliwatt; +30dbm is one watt. Don't be misled by the oft-quoted "0dBm = one milliwatt in 600 ohms". This is an artifact of when voltage measuring devices were used to display dBm. Because they were voltage measuring rather than power measuring, an impedance at which they read correctly needed to be specified, and it was nearly always 600 ohms.
Over the years dB usage has been stretched to cover situations where having a logarithmic unit is really useful. For instance the voltage gain of an amplifier may be quoted in dB, using the formula 20log(Vout/Vin). In this situation, the input and output impedances (and hence powers) are often vastly different, so the usage is technically wrong. In practice it is a convenient unit to work with, and has been given some legitimacy by labeling it dBv.
The first formula you are using will return dB referenced to 1 volt in whatever impedance your circuit exhibits. This is fine, but it won't be dBm. Often this does not matter, as you just need to graph gain in dB against an arbitrary reference.If you need it to be dBm just find the circuit impedance and use Ohms law to work out what voltage represents one milliwatt in that impedance.
The second formula is a bit strange. What is x2? I would expect the formula to be 20log(Vmeasured/Vreference).
I need to get an accurate measurement of altitude using GPS only.
I tried Location.getAltitude(), but that is terribly inaccurate.
Any advice?
There are two issues with using altitude of a smartphone / tablet GPS:
The altitude is the altitude above the WGS84 reference ellipsoid. It is not the altitude above ground level or sea level. Here is more detail on that: http://www.gpspassion.com/forumsen/topic.asp?TOPIC_ID=10915. This error can be corrected; here is a description how to do that by hand: http://www.unavco.org/edu_outreach/tutorial/geoidcorr.html. The web article links to a calculator to get the Geoid height for correction; I do not know if there is also a web service available for this computation.
The GPS altitude is terribly inaccurate for relatively cheap GPS receivers. Here is an article on that: http://gpsinformation.net/main/altitude.htm. One method to cope with this kind of inaccuracy is to filter the altitude data. I used a circular array data structure to remember the last few (I used 4) altitude readings and compute the average. This sufficed to get a relatively accurate reading of vertical speed for my application.
Another way would be parsing NMEA strings. The $GPGGA sentence already contains the corrected altitude data above sea level.
So, simply create a listener to NMEA-strings for your LocationManager and parse the messages:
private GpsStatus.NmeaListener mNmeaListener = new GpsStatus.NmeaListener() {
#Override
public void onNmeaReceived(long timestamp, String nmea) {
parseNmeaString(nmea);
}
};
public void registerLocationManager(Context context) {
mLocationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
mLocationManager.addNmeaListener(mNmeaListener);
}
private void parseNmeaString(String line) {
if (line.startsWith("$")) {
String[] tokens = line.split(",");
String type = tokens[0];
// Parse altitude above sea level, Detailed description of NMEA string here http://aprs.gids.nl/nmea/#gga
if (type.startsWith("$GPGGA")) {
if (!tokens[9].isEmpty()) {
mLastMslAltitude = Double.parseDouble(tokens[9]);
}
}
}
}
You can either replace the altitude of the last Location object received through a location listener, or parse the whole new location through NMEA.
Another approach is to measure the altitude from the barometer.
By using the pressure you can calculate the user's altitude. I'm uncertain of the precision level of this and whether it is more accurate than the other answer's approach.
By using the hypsometric formula you can calculate the altitude:
Variable definition:
P0: Sea-level pressure
P: Atmospheric pressure
T: Temperature
You can get the pressure in android from the environment sensors
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,pressure)
If the device has a barometer, then use can that to improve the relative accuracy. I don't mean to use the barometer to compute the height in relation to the sea level, as can be found in several formulas, as this is highly dependent of the weather conditions.
What you want to do is to get the GPS-altitude when you know that the device has a good fix, with high accuracy values. At that point you fetch the barometric pressure and set that pressure as a reference.
When the pressure increases around 12 hPa, you will know that your altitude decreased by around 100 m ( https://en.wikipedia.org/wiki/Atmospheric_pressure "At low altitudes above sea level, the pressure decreases by about 1.2 kPa (12 hPa) for every 100 metres." ).
Don't take that value as an exact, but variations in the altitude determined by GPS vary a lot due to trees covering the line of sight and other factors, while the barometer will remain very precise under those conditions.
The following graph is a bike ride of around one hour and 20 minutes in duration. Starting point and end point are the same at around 477 m above sea level, determined via GPS. The measured pressure is 1015.223 hPA.
The lowest point is 377 m, with a measured pressure of 1025.119 hPa. So in that case, 100 m make a difference of 10 hPa.
The highest point is 550 m, with a measured pressure of 1007.765 hPa.
Ending point is the same height, and the exact same pressure as the starting conditions (the pressure could have varied due to the weather conditions, but it didn't). The temperature dropped around 1°C, so it was all pretty constant.
The black line containing the many variations is the altitude measured via GPS, the mirrored, but clean line, is the barometric pressure. It has very little variation in it simply because the pressure doesn't vary as wild as the GPS-quality. It is a very smooth, very precise curve. This is not due to lag. This is measured with a Bosch BME280 sensor, which is capable of detecting the closing of a door, change of floor detection, elevator direction, drones, with a noise of 0.2 Pa which equals 1.7 cm and an error of 1m at 400m of height change. These kind of sensors are integrated in some Smartphones. For example, the Pixel 3 contains a Bosch BMP380.
If you mirror the pressure graph, as is shown with the dotted black line, you will see that it very closely matches the GPS altitude. It is much more precise than GPS, but the only problem is that you can't take it as an absolute value.
The samples of GPS and pressure were both taken in 1 second intervals, so there is no curve smoothing from the d3 library causing some false impressions.
So maybe readjusting the pressure around every 10-30 minutes while you have a GPS good fix will give you a good base to perform your altitude measurements by pressure in between.
There are other ways to get the altitude than by GPS. You can use the barometer but as there isn't that many devices with a barometric sensors yet (only the new ones). I will recommend to use a web service to acquire the desired data.
Here is a question which should help you through: Get altitude by longitude and latitude in Android
For newcomers I made a library that wrap LocationManager into rxjava observables and add some observable helpers to get sea level altitutde from Nmea/GPGGA mre info here
There are libraries, such as the open-source CS-Map which provide an API that do these lookups in large tables. You specify the coordinates, and it will tell you the height offset that needs to be applied to the ellipsoidal height at that location to get the "real-world" orthometric height.
Note: Having used CS-Map before, it isn't exactly straight-forward 5-minute job to plug it in. Warning you in advance that it is more complicated than calling a single API with a set of lat-long coordinates and getting back a height. I no longer work at the company where we were doing this kind of work, so unfortunately cannot look up the code to say exactly which API to call.
Doing a google search for it right now (2019), it seems CS-Map has been merged into MetaCRS in OSGeo, the "Open Source Geospatial Foundation" project. That same search led me to this old CS-Map wiki as well as the PROJ GitHub page, where PROJ seems to be similar project to CS-Map.
I would recommend using NmeaListener, as sladstaetter
suggested in his answer. However, according to NMEA documentation, "$GPGGA" is not the only sentence you can get. You should look for any "GGA" sentence ($--GGA).
Regular expressions are great for that, e.g.:
#Override
public void onNmeaReceived(final long timestamp, final String nmea) {
final Pattern pattern = Pattern.compile("\\$..GGA,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([+-]?\\d+(.\\d+)?),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$");
final Matcher matcher = pattern.matcher(nmea);
if (matcher.find()) {
final float altitude = Float.parseFloat(matcher.group(1));
// ...enjoy the altitude!
}
}
I am getting negative altitude value when finding latitude , longitude and altitude. Can anyone help me to find the reason for it ?
Short of seeing the code which produces these values, there's not much I can offer beyond general knowledge.
GPS altitudes generally use a geodetic model for an idealised sea level (the zero altitude), basically mapping an ellipsoid onto a less-than-perfectly-shaped planet (which varies anyway with things such as lunar tidal forces). See WGS84 here for more details.
The normal error is about +/- 15m, and this only applies about 95% of the time. According to the specs I remember, the other 5% of the time can have an altitude of any value.
And, of course, if your GPS doesn't have an unobstructed pathway between it and the satellites, the error range is much greater.
These potential errors are no doubt why aircraft rely more on altimeters or ILS for their near-ground activities.
As one site puts it:
What this means is that if you are walking on the seashore, and see your altitude as -15 meters, you should not be concerned.
Of course, if you're having troubles breathing, you may want to look more closely at how wet you are :-)
Well, altitude in context of GPS coordinates represents your elevation according to the sea level. So I assume your current position is just below the sea level. Yeah, this is possible ;)
The GPS altitude is the altitude above the GPS WGS84 reference elipsoid (which is different from sea level and does not take hills into account!). A negative altutude means (if it not due to a bad signal) that you are below the reference elipsoid.
Here is more information on how to get more accurate altitude: https://stackoverflow.com/a/9432382/1127492
Switch positioning settings to use Geoid model.
I need to get an accurate measurement of altitude using GPS only.
I tried Location.getAltitude(), but that is terribly inaccurate.
Any advice?
There are two issues with using altitude of a smartphone / tablet GPS:
The altitude is the altitude above the WGS84 reference ellipsoid. It is not the altitude above ground level or sea level. Here is more detail on that: http://www.gpspassion.com/forumsen/topic.asp?TOPIC_ID=10915. This error can be corrected; here is a description how to do that by hand: http://www.unavco.org/edu_outreach/tutorial/geoidcorr.html. The web article links to a calculator to get the Geoid height for correction; I do not know if there is also a web service available for this computation.
The GPS altitude is terribly inaccurate for relatively cheap GPS receivers. Here is an article on that: http://gpsinformation.net/main/altitude.htm. One method to cope with this kind of inaccuracy is to filter the altitude data. I used a circular array data structure to remember the last few (I used 4) altitude readings and compute the average. This sufficed to get a relatively accurate reading of vertical speed for my application.
Another way would be parsing NMEA strings. The $GPGGA sentence already contains the corrected altitude data above sea level.
So, simply create a listener to NMEA-strings for your LocationManager and parse the messages:
private GpsStatus.NmeaListener mNmeaListener = new GpsStatus.NmeaListener() {
#Override
public void onNmeaReceived(long timestamp, String nmea) {
parseNmeaString(nmea);
}
};
public void registerLocationManager(Context context) {
mLocationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
mLocationManager.addNmeaListener(mNmeaListener);
}
private void parseNmeaString(String line) {
if (line.startsWith("$")) {
String[] tokens = line.split(",");
String type = tokens[0];
// Parse altitude above sea level, Detailed description of NMEA string here http://aprs.gids.nl/nmea/#gga
if (type.startsWith("$GPGGA")) {
if (!tokens[9].isEmpty()) {
mLastMslAltitude = Double.parseDouble(tokens[9]);
}
}
}
}
You can either replace the altitude of the last Location object received through a location listener, or parse the whole new location through NMEA.
Another approach is to measure the altitude from the barometer.
By using the pressure you can calculate the user's altitude. I'm uncertain of the precision level of this and whether it is more accurate than the other answer's approach.
By using the hypsometric formula you can calculate the altitude:
Variable definition:
P0: Sea-level pressure
P: Atmospheric pressure
T: Temperature
You can get the pressure in android from the environment sensors
SensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE,pressure)
If the device has a barometer, then use can that to improve the relative accuracy. I don't mean to use the barometer to compute the height in relation to the sea level, as can be found in several formulas, as this is highly dependent of the weather conditions.
What you want to do is to get the GPS-altitude when you know that the device has a good fix, with high accuracy values. At that point you fetch the barometric pressure and set that pressure as a reference.
When the pressure increases around 12 hPa, you will know that your altitude decreased by around 100 m ( https://en.wikipedia.org/wiki/Atmospheric_pressure "At low altitudes above sea level, the pressure decreases by about 1.2 kPa (12 hPa) for every 100 metres." ).
Don't take that value as an exact, but variations in the altitude determined by GPS vary a lot due to trees covering the line of sight and other factors, while the barometer will remain very precise under those conditions.
The following graph is a bike ride of around one hour and 20 minutes in duration. Starting point and end point are the same at around 477 m above sea level, determined via GPS. The measured pressure is 1015.223 hPA.
The lowest point is 377 m, with a measured pressure of 1025.119 hPa. So in that case, 100 m make a difference of 10 hPa.
The highest point is 550 m, with a measured pressure of 1007.765 hPa.
Ending point is the same height, and the exact same pressure as the starting conditions (the pressure could have varied due to the weather conditions, but it didn't). The temperature dropped around 1°C, so it was all pretty constant.
The black line containing the many variations is the altitude measured via GPS, the mirrored, but clean line, is the barometric pressure. It has very little variation in it simply because the pressure doesn't vary as wild as the GPS-quality. It is a very smooth, very precise curve. This is not due to lag. This is measured with a Bosch BME280 sensor, which is capable of detecting the closing of a door, change of floor detection, elevator direction, drones, with a noise of 0.2 Pa which equals 1.7 cm and an error of 1m at 400m of height change. These kind of sensors are integrated in some Smartphones. For example, the Pixel 3 contains a Bosch BMP380.
If you mirror the pressure graph, as is shown with the dotted black line, you will see that it very closely matches the GPS altitude. It is much more precise than GPS, but the only problem is that you can't take it as an absolute value.
The samples of GPS and pressure were both taken in 1 second intervals, so there is no curve smoothing from the d3 library causing some false impressions.
So maybe readjusting the pressure around every 10-30 minutes while you have a GPS good fix will give you a good base to perform your altitude measurements by pressure in between.
There are other ways to get the altitude than by GPS. You can use the barometer but as there isn't that many devices with a barometric sensors yet (only the new ones). I will recommend to use a web service to acquire the desired data.
Here is a question which should help you through: Get altitude by longitude and latitude in Android
For newcomers I made a library that wrap LocationManager into rxjava observables and add some observable helpers to get sea level altitutde from Nmea/GPGGA mre info here
There are libraries, such as the open-source CS-Map which provide an API that do these lookups in large tables. You specify the coordinates, and it will tell you the height offset that needs to be applied to the ellipsoidal height at that location to get the "real-world" orthometric height.
Note: Having used CS-Map before, it isn't exactly straight-forward 5-minute job to plug it in. Warning you in advance that it is more complicated than calling a single API with a set of lat-long coordinates and getting back a height. I no longer work at the company where we were doing this kind of work, so unfortunately cannot look up the code to say exactly which API to call.
Doing a google search for it right now (2019), it seems CS-Map has been merged into MetaCRS in OSGeo, the "Open Source Geospatial Foundation" project. That same search led me to this old CS-Map wiki as well as the PROJ GitHub page, where PROJ seems to be similar project to CS-Map.
I would recommend using NmeaListener, as sladstaetter
suggested in his answer. However, according to NMEA documentation, "$GPGGA" is not the only sentence you can get. You should look for any "GGA" sentence ($--GGA).
Regular expressions are great for that, e.g.:
#Override
public void onNmeaReceived(final long timestamp, final String nmea) {
final Pattern pattern = Pattern.compile("\\$..GGA,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([+-]?\\d+(.\\d+)?),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$");
final Matcher matcher = pattern.matcher(nmea);
if (matcher.find()) {
final float altitude = Float.parseFloat(matcher.group(1));
// ...enjoy the altitude!
}
}
I am using the AudioRecord class to analize raw pcm bytes as it comes in the mic.
So thats working nicely. Now i need convert the pcm bytes into decibel.
I have a formula that takes sound presure in Pa into db.
db = 20 * log10(Pa/ref Pa)
So the question is the bytes i am getting from audiorecorder from the buffer what is it is it amplitude pascal sound pressure or what.
I tried to putting the value into te formula but it comes back with very hight db so i do not think its right
thanks
Disclaimer: I know little about Android.
Your device is probably recording in mono at 44,100 samples per second (maybe less) using two bytes per sample. So your first step is to combine pairs of bytes in your original data into two-byte integers (I don't know how this is done in Android).
You can then compute the decibel value (relative to the peak) of each sample by first taking the normalized absolute value of the sample and passing it to your Db function:
float Db = 20 * log10(ABS(sampleVal) / 32768)
A value near the peak (e.g. +32767 or -32768) will have a Db value near 0. A value of 3277 (0.1) will have a Db value of -20; a value of 327 (.01) will have a Db value of -40 etc.
The problem is likely the definition of the "reference" sound pressure at the mic. I have no idea what it would be or if it's available.
The only audio application I've ever used, defined 0db as "full volume", when the samples were at + or - max value (in unsigned 16 bits, that'd be 0 and 65535). To get this into db I'd probably do something like this:
// assume input_sample is in the range 0 to 65535
sample = (input_sample * 10.0) - 327675.0
db = log10(sample / 327675.0)
I don't know if that's right, but it feels right to the mathematically challenged me. As the input_sample approaches the "middle", it'll look more and more like negative infinity.
Now that I think about it, though, if you want a SPL or something that might require different trickery like doing RMS evaluation between the zero crossings, again something that I could only guess at because I have no idea how it really works.
The reference pressure in Leq (sound pressure level) calculations is 20 micro-Pascal (rms).
To measure absolute Leq levels, you need to calibrate your microphone using a calibrator. Most calibrators fit 1/2" or 1/4" microphone capsules, so I have my doubts about calibrating the microphone on an Android phone. Alternatively you may be able to use the microphone sensitivity (Pa/mV) and then calibrate the voltage level going into the ADC. Even less reliable results could be had from comparing the Android values with the measured sound level of a diffuse stationary sound field using a sound level meter.
Note that in Leq calculations you normally use the RMS values. A single sample's value doesn't mean much.
I held my sound level meter right next to the mic on my google ion and went 'Woooooo!' and noted that clipping occurred about 105 db spl. Hope this helps.
The units are whatever units are used for the reference reading. In the formula, the reading is divided by the reference reading, so the units cancel out and no longer matter.
In other words, decibels is a way of comparing two things, it is not an absolute measurement. When you see it used as if it is absolute, then the comparison is with the quietest sound the average human can hear.
In our case, it is a comparison to the highest reading the device handles (thus, every other reading is negative, or less than the maximum).