Problems with TMinuit - root-framework

I'm trying to minimize a chi square function in ROOT, this is the FCN code:
void myFCNres( int &npar , double * gin , double &f , double * par , int flag ) {
double chi2 = 0;
for ( int i = 1 ; i <= datasetNHres->GetSize()-2 ; ++i ) {
par[7] = Emin + (Emax-Emin)/(2*N_div) + (i-1)*(Emax-Emin)/N_div;
chi2 += pow( ( integrand->Integral( 1.804 , 8 , par , 1E-15) - datasetNHres->GetBinContent(i)) / datasetNHres->GetBinError(i) ,2);
}
chi2 += pow( (par[0] - p[0]) / sig[0] ,2) +
pow( (par[1] - p[1]) / sig[1] ,2) +
pow( (par[2] - p[2]) / sig[2] ,2) +
pow( (par[3] + p[3]) / sig[3] ,2);
f = chi2;
}
The result of running is that none of the parameters has changed, just like my function does not depend on any of them. Do you see problems with FCN logic or I have to look for the bug elsewhere?
Thanks,
Luigi

Related

Look-at quaternion using up vector

I have a camera (in a custom 3D engine) that accepts a quaternion for the rotation transform. I have two 3D points representing a camera and an object to look at. I want to calculate the quaternion that looks from the camera to the object, while respecting the world up axis.
This question asks for the same thing without the "up" vector. All three answers result in the camera pointing in the correct direction, but rolling (as in yaw/pitch/roll; imagine leaning your head onto your ear while looking at something).
I can calculate an orthonormal basis of vectors that match the desired coordinate system by:
lookAt = normalize(target - camera)
sideaxis = cross(lookAt, worldUp)
rotatedup = cross(sideaxis, lookAt)
How can I create a quaternion from those three vectors? This question asks for the same thing...but unfortunately the only and accepted answer says ~"let's assume you don't care about roll", and then goes about ignoring the up axis. I do care about roll. I don't want to ignore the up axis.
A previous answer has given a valid solution using angles. This answer will present an alternative method.
The orthonormal basis vectors, renaming them F = lookAt, R = sideaxis, U = rotatedup, directly form the columns of the 3x3 rotation matrix which is equivalent to your desired quaternion:
Multiplication with a vector is equivalent to using said vector's components as the coordinates in the camera's basis.
A 3x3 rotation matrix can be converted into a quaternion without conversion to angles / use of costly trigonometric functions. Below is a numerically stable C++ snippet which does this, returning a normalized quaternion:
inline void CalculateRotation( Quaternion& q ) const {
float trace = a[0][0] + a[1][1] + a[2][2];
if( trace > 0 ) {
float s = 0.5f / sqrtf(trace + 1.0f);
q.w = 0.25f / s;
q.x = ( a[2][1] - a[1][2] ) * s;
q.y = ( a[0][2] - a[2][0] ) * s;
q.z = ( a[1][0] - a[0][1] ) * s;
} else {
if ( a[0][0] > a[1][1] && a[0][0] > a[2][2] ) {
float s = 2.0f * sqrtf( 1.0f + a[0][0] - a[1][1] - a[2][2]);
q.w = (a[2][1] - a[1][2] ) / s;
q.x = 0.25f * s;
q.y = (a[0][1] + a[1][0] ) / s;
q.z = (a[0][2] + a[2][0] ) / s;
} else if (a[1][1] > a[2][2]) {
float s = 2.0f * sqrtf( 1.0f + a[1][1] - a[0][0] - a[2][2]);
q.w = (a[0][2] - a[2][0] ) / s;
q.x = (a[0][1] + a[1][0] ) / s;
q.y = 0.25f * s;
q.z = (a[1][2] + a[2][1] ) / s;
} else {
float s = 2.0f * sqrtf( 1.0f + a[2][2] - a[0][0] - a[1][1] );
q.w = (a[1][0] - a[0][1] ) / s;
q.x = (a[0][2] + a[2][0] ) / s;
q.y = (a[1][2] + a[2][1] ) / s;
q.z = 0.25f * s;
}
}
}
Source: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion
Converting this to suit your situation is of course just a matter of swapping the matrix elements with the corresponding vector components:
// your code from before
F = normalize(target - camera); // lookAt
R = normalize(cross(F, worldUp)); // sideaxis
U = cross(R, F); // rotatedup
// note that R needed to be re-normalized
// since F and worldUp are not necessary perpendicular
// so must remove the sin(angle) factor of the cross-product
// same not true for U because dot(R, F) = 0
// adapted source
Quaternion q;
double trace = R.x + U.y + F.z;
if (trace > 0.0) {
double s = 0.5 / sqrt(trace + 1.0);
q.w = 0.25 / s;
q.x = (U.z - F.y) * s;
q.y = (F.x - R.z) * s;
q.z = (R.y - U.x) * s;
} else {
if (R.x > U.y && R.x > F.z) {
double s = 2.0 * sqrt(1.0 + R.x - U.y - F.z);
q.w = (U.z - F.y) / s;
q.x = 0.25 * s;
q.y = (U.x + R.y) / s;
q.z = (F.x + R.z) / s;
} else if (U.y > F.z) {
double s = 2.0 * sqrt(1.0 + U.y - R.x - F.z);
q.w = (F.x - R.z) / s;
q.x = (U.x + R.y) / s;
q.y = 0.25 * s;
q.z = (F.y + U.z) / s;
} else {
double s = 2.0 * sqrt(1.0 + F.z - R.x - U.y);
q.w = (R.y - U.x) / s;
q.x = (F.x + R.z) / s;
q.y = (F.y + U.z) / s;
q.z = 0.25 * s;
}
}
(And needless to say swap y and z if you're using OpenGL.)
Assume you initially have three ortonormal vectors: worldUp, worldFront and worldSide, and lets use your equations for lookAt, sideAxis and rotatedUp. The worldSide vector will not be necessary to achieve the result.
Break the operation in two. First, rotate around worldUp. Then rotate around sideAxis, which will now actually be parallel to the rotated worldSide.
Axis1 = worldUp
Angle1 = (see below)
Axis2 = cross(lookAt, worldUp) = sideAxis
Angle2 = (see below)
Each of these rotations correspond to a quaternion using:
Q = cos(Angle/2) + i * Axis_x * sin(Angle/2) + j * Axis_y * sin(Angle/2) + k * Axis_z * sin(Angle/2)
Multiply both Q1 and Q2 and you get the desired quaternion.
Details for the angles:
Let P(worldUp) be the projection matrix on the worldUp direction, i.e., P(worldUp).v = cos(worldUp,v).worldUp or using kets and bras, P(worldUp) = |worldUp >< worldUp|. Let I be the identity matrix.
Project lookAt in the plane perpendicular to worldUp and normalize it.
tmp1 = (I - P(worldUp)).lookAt
n1 = normalize(tmp1)
Angle1 = arccos(dot(worldFront,n1))
Angle2 = arccos(dot(lookAt,n1))
EDIT1:
Notice that there is no need to compute transcendental functions. Since the dot product of a pair of normalized vectors is the cosine of an angle and assuming that cos(t) = x, we have the trigonometric identities:
cos(t/2) = sqrt((1 + x)/2)
sin(t/2) = sqrt((1 - x)/2)
If somebody search for C# version with handling every matrix edge cases (not input edge cases!), here it is:
public static SoftQuaternion LookRotation(SoftVector3 forward, SoftVector3 up)
{
forward = SoftVector3.Normalize(forward);
// First matrix column
SoftVector3 sideAxis = SoftVector3.Normalize(SoftVector3.Cross(up, forward));
// Second matrix column
SoftVector3 rotatedUp = SoftVector3.Cross(forward, sideAxis);
// Third matrix column
SoftVector3 lookAt = forward;
// Sums of matrix main diagonal elements
SoftFloat trace1 = SoftFloat.One + sideAxis.X - rotatedUp.Y - lookAt.Z;
SoftFloat trace2 = SoftFloat.One - sideAxis.X + rotatedUp.Y - lookAt.Z;
SoftFloat trace3 = SoftFloat.One - sideAxis.X - rotatedUp.Y + lookAt.Z;
// If orthonormal vectors forms identity matrix, then return identity rotation
if (trace1 + trace2 + trace3 < SoftMath.CalculationsEpsilon)
{
return Identity;
}
// Choose largest diagonal
if (trace1 + SoftMath.CalculationsEpsilon > trace2 && trace1 + SoftMath.CalculationsEpsilon > trace3)
{
SoftFloat s = SoftMath.Sqrt(trace1) * (SoftFloat)2.0f;
return new SoftQuaternion(
(SoftFloat)0.25f * s,
(rotatedUp.X + sideAxis.Y) / s,
(lookAt.X + sideAxis.Z) / s,
(rotatedUp.Z - lookAt.Y) / s);
}
else if (trace2 + SoftMath.CalculationsEpsilon > trace1 && trace2 + SoftMath.CalculationsEpsilon > trace3)
{
SoftFloat s = SoftMath.Sqrt(trace2) * (SoftFloat)2.0f;
return new SoftQuaternion(
(rotatedUp.X + sideAxis.Y) / s,
(SoftFloat)0.25f * s,
(lookAt.Y + rotatedUp.Z) / s,
(lookAt.X - sideAxis.Z) / s);
}
else
{
SoftFloat s = SoftMath.Sqrt(trace3) * (SoftFloat)2.0f;
return new SoftQuaternion(
(lookAt.X + sideAxis.Z) / s,
(lookAt.Y + rotatedUp.Z) / s,
(SoftFloat)0.25f * s,
(sideAxis.Y - rotatedUp.X) / s);
}
}
This realization based on deeper understanding of this conversation, and was tested for many edge case scenarios.
P.S.
Quaternion's constructor is (x, y, z, w)
SoftFloat is software float type, so you can easyly change it to built-in float if needed
For full edge case safe realization (including input) check this repo.
lookAt
sideaxis
rotatedup
If you normalize this 3 vectors, it is a components of rotation matrix 3x3. So just convert this rotation matrix to quaternion.

Accurately calculate moon phases

For a new project I like to calculate the moon phases. So far I haven't seen any code that does that. I don't want to rely on online-services for this.
I have tried some functions, but they are not 100% reliable. Functions I have tried:
NSInteger r = iYear % 100;
r %= 19;
if (r>9){ r -= 19;}
r = ((r * 11) % 30) + iMonth + iDay;
if (iMonth<3){r += 2;}
r -= ((iYear<2000) ? 4 : 8.3);
r = floor(r+0.5);
other one:
float n = floor(12.37 * (iYear -1900 + ((1.0 * iMonth - 0.5)/12.0)));
float RAD = 3.14159265/180.0;
float t = n / 1236.85;
float t2 = t * t;
float as = 359.2242 + 29.105356 * n;
float am = 306.0253 + 385.816918 * n + 0.010730 * t2;
float xtra = 0.75933 + 1.53058868 * n + ((1.178e-4) - (1.55e-7) * t) * t2;
xtra = xtra + (0.1734 - 3.93e-4 * t) * sin(RAD * as) - 0.4068 * sin(RAD * am);
float i = (xtra > 0.0 ? floor(xtra) : ceil(xtra - 1.0));
float j1 = [self julday:iYear iMonth:iMonth iDay:iDay];
float jd = (2415020 + 28 * n) + i;
jd = fmodf((j1-jd + 30), 30);
and last one
NSInteger thisJD = [self julday:iYear iMonth:iMonth iDay:iDay];
float degToRad = 3.14159265 / 180;
float K0, T, T2, T3, J0, F0, M0, M1, B1, oldJ = 0.0;
K0 = floor((iYear-1900)*12.3685);
T = (iYear-1899.5) / 100;
T2 = T*T; T3 = T*T*T;
J0 = 2415020 + 29*K0;
F0 = 0.0001178*T2 - 0.000000155*T3 + (0.75933 + 0.53058868*K0) - (0.000837*T + 0.000335*T2);
M0 = 360*[self getFrac:((K0*0.08084821133)) + 359.2242 - 0.0000333*T2 - 0.00000347*T3];
M1 = 360*[self getFrac:((K0*0.07171366128)) + 306.0253 + 0.0107306*T2 + 0.00001236*T3];
B1 = 360*[self getFrac:((K0*0.08519585128)) + 21.2964 - (0.0016528*T2) - (0.00000239*T3)];
NSInteger phase = 0;
NSInteger jday = 0;
while (jday < thisJD) {
float F = F0 + 1.530588*phase;
float M5 = (M0 + phase*29.10535608)*degToRad;
float M6 = (M1 + phase*385.81691806)*degToRad;
float B6 = (B1 + phase*390.67050646)*degToRad;
F -= 0.4068*sin(M6) + (0.1734 - 0.000393*T)*sin(M5);
F += 0.0161*sin(2*M6) + 0.0104*sin(2*B6);
F -= 0.0074*sin(M5 - M6) - 0.0051*sin(M5 + M6);
F += 0.0021*sin(2*M5) + 0.0010*sin(2*B6-M6);
F += 0.5 / 1440;
oldJ=jday;
jday = J0 + 28*phase + floor(F);
phase++;
}
float jd = fmodf((thisJD-oldJ), 30);
All are working more and less, but none is really giving the correct dates of full moon for 2017 and 2018.
Does anyone have a function that will calculate the moon phases correctly - also based on time zone?
EDIT:
I only want the function for the Moonphases. SwiftAA offers a lot more and only produces not needed overhead in the app.

calculate angle with three CGpoint

I have three CGpoint and I would like calculate the angle.
I drawn a little schema :
I tried with this code :
CGPoint u ;
u.x = 0;
u.y = - middleRectY;
CGPoint v ;
v.x = x1 - middelRectX;
v.y = y1 - middleRectY;
// formule = u.v / ( ||u|| * ||v||)
double cosa = (double)((u.x * v.x + u.y * v.y)) / sqrt(u.x * u.x + u.y * u.y) * sqrt(v.x * v.x + v.y * v.y);
// angle en degré
double angle = (180.0 / M_PI) * acos(cosa);
// Signe de l'angle
int sign = (u.x * v.y - u.y * v.x) > 0 ? 1 : -1;
rectYellow.transform = CGAffineTransformMakeRotation(angle*sign);
But my function return "nan" :/
Thx :)
I found the problem !
It just a probleme of parenthesis :
double cosa = ((u.x * v.x) + (u.y * v.y)) / (sqrt((u.x * u.x) + (u.y * u.y)) * sqrt((v.x * v.x) + (v.y * v.y)));
I don't understand why ?
Because the parentheses aren't necessary for multiplication ...

OpenACC red-black Gauss-Seidel slower than CPU

I added OpenACC directives to my red-black Gauss-Seidel solver for the Laplace equation (a simple heated plate problem), but the GPU-accelerated code is no faster than the CPU, even for large problems.
I also wrote a CUDA version, and that is much faster than both (for 512x512, on the order of 2 seconds compared to 25 for CPU and OpenACC).
Can anyone think of a reason for this discrepancy? I realize that CUDA offers the most potential speed, but OpenACC should give something better than the CPU for larger problems (like the Jacobi solver for the same sort of problem demonstrated here).
Here is the relevant code (the full working source is here):
#pragma acc data copyin(aP[0:size], aW[0:size], aE[0:size], aS[0:size], aN[0:size], b[0:size]) copy(temp_red[0:size_temp], temp_black[0:size_temp])
// red-black Gauss-Seidel with SOR iteration loop
for (iter = 1; iter <= it_max; ++iter) {
Real norm_L2 = 0.0;
// update red cells
#pragma omp parallel for shared(aP, aW, aE, aS, aN, temp_black, temp_red) \
reduction(+:norm_L2)
#pragma acc kernels present(aP[0:size], aW[0:size], aE[0:size], aS[0:size], aN[0:size], b[0:size], temp_red[0:size_temp], temp_black[0:size_temp])
#pragma acc loop independent gang vector(4)
for (int col = 1; col < NUM + 1; ++col) {
#pragma acc loop independent gang vector(64)
for (int row = 1; row < (NUM / 2) + 1; ++row) {
int ind_red = col * ((NUM / 2) + 2) + row; // local (red) index
int ind = 2 * row - (col % 2) - 1 + NUM * (col - 1); // global index
#pragma acc cache(aP[ind], b[ind], aW[ind], aE[ind], aS[ind], aN[ind])
Real res = b[ind] + (aW[ind] * temp_black[row + (col - 1) * ((NUM / 2) + 2)]
+ aE[ind] * temp_black[row + (col + 1) * ((NUM / 2) + 2)]
+ aS[ind] * temp_black[row - (col % 2) + col * ((NUM / 2) + 2)]
+ aN[ind] * temp_black[row + ((col + 1) % 2) + col * ((NUM / 2) + 2)]);
Real temp_old = temp_red[ind_red];
temp_red[ind_red] = temp_old * (1.0 - omega) + omega * (res / aP[ind]);
// calculate residual
res = temp_red[ind_red] - temp_old;
norm_L2 += (res * res);
} // end for row
} // end for col
// update black cells
#pragma omp parallel for shared(aP, aW, aE, aS, aN, temp_black, temp_red) \
reduction(+:norm_L2)
#pragma acc kernels present(aP[0:size], aW[0:size], aE[0:size], aS[0:size], aN[0:size], b[0:size], temp_red[0:size_temp], temp_black[0:size_temp])
#pragma acc loop independent gang vector(4)
for (int col = 1; col < NUM + 1; ++col) {
#pragma acc loop independent gang vector(64)
for (int row = 1; row < (NUM / 2) + 1; ++row) {
int ind_black = col * ((NUM / 2) + 2) + row; // local (black) index
int ind = 2 * row - ((col + 1) % 2) - 1 + NUM * (col - 1); // global index
#pragma acc cache(aP[ind], b[ind], aW[ind], aE[ind], aS[ind], aN[ind])
Real res = b[ind] + (aW[ind] * temp_red[row + (col - 1) * ((NUM / 2) + 2)]
+ aE[ind] * temp_red[row + (col + 1) * ((NUM / 2) + 2)]
+ aS[ind] * temp_red[row - ((col + 1) % 2) + col * ((NUM / 2) + 2)]
+ aN[ind] * temp_red[row + (col % 2) + col * ((NUM / 2) + 2)]);
Real temp_old = temp_black[ind_black];
temp_black[ind_black] = temp_old * (1.0 - omega) + omega * (res / aP[ind]);
// calculate residual
res = temp_black[ind_black] - temp_old;
norm_L2 += (res * res);
} // end for row
} // end for col
// calculate residual
norm_L2 = sqrt(norm_L2 / ((Real)size));
if(iter % 100 == 0) printf("%5d, %0.6f\n", iter, norm_L2);
// if tolerance has been reached, end SOR iterations
if (norm_L2 < tol) {
break;
}
}
Alright, I found a semi-solution that reduces the time somewhat significantly for smaller problems.
If I insert the lines:
acc_init(acc_device_nvidia);
acc_set_device_num(0, acc_device_nvidia);
before I start my timer, in order to activate and set the GPU, the time for the 512x512 problem drops to 9.8 seconds, and down to 42 for 1024x1024. Increasing the problem size further shows how fast even OpenACC can be compared to running on four CPU cores.
With this change, the OpenACC code is on the order of 2x slower than the CUDA code, with the gap getting closer to just a bit slower (~1.2) as the problem size gets bigger and bigger.
I download your full code and i compiled and run it! Did't stop run and for instruction
if(iter % 100 == 0) printf("%5d, %0.6f\n", iter, norm_L2);
the result was:
100, nan
200, nan
....
I changed all variables with type Real into type float and the result was:
100, 0.000654
200, 0.000370
..., ....
..., ....
8800, 0.000002
8900, 0.000002
9000, 0.000001
9100, 0.000001
9200, 0.000001
9300, 0.000001
9400, 0.000001
9500, 0.000001
9600, 0.000001
9700, 0.000001
CPU
Iterations: 9796
Total time: 5.594017 s
With NUM = 1024 the result was:
Iterations: 27271
Total time: 25.949905 s

SQL function to convert UK OS coordinates from easting/northing to longitude and latitude

Please can someone post a SQL function to convert easting/northing to longitude/latitude. I know it's incredibly complicated but I haven't found anyone who has documented it in T-SQL.
This javascript code works but I'm having trouble converting it to SQL.
I have 16,000 coordinates and need them all converted to lat/long.
This is what I have so far but it's not getting past the while loop.
DECLARE #east real = 482353,
#north real = 213371
DECLARE #a real = 6377563.396,
#b real = 6356256.910,
#F0 real = 0.9996012717,
#lat0 real = 49*PI()/180,
#lon0 real = -2*PI()/180
DECLARE #N0 real = -100000,
#E0 real = 400000,
#e2 real = 1 - (#b*#b)/(#a*#a),
#n real = (#a-#b)/(#a+#b)
DECLARE #n2 real = #n*#n,
#n3 real = #n*#n*#n
DECLARE #lat real = #lat0,
#M real = 0
WHILE (#north-#N0-#M >= 0.00001)
BEGIN
SET #lat = ((#north-#N0-#M)/(#a*#F0)) + #lat
DECLARE #Ma real = (1 + #n + (5/4)*#n2 + (5/4)*#n3) * (#lat-#lat0),
#Mb real = (3*#n + 3*#n*#n + (21/8)*#n3) * SIN(#lat-#lat0) * COS(#lat+#lat0),
#Mc real = ((15/8)*#n2 + (15/8)*#n3) * SIN(2*(#lat-#lat0)) * COS(2*(#lat+#lat0)),
#Md real = (35/24)*#n3 * SIN(3*(#lat-#lat0)) * COS(3*(#lat+#lat0))
SET #M = #b * #F0 * (#Ma - #Mb + #Mc - #Md)
END
DECLARE #cosLat real = COS(#lat),
#sinLat real = SIN(#lat)
DECLARE #nu real = #a*#F0/sqrt(1-#e2*#sinLat*#sinLat)
DECLARE #rho real = #a*#F0*(1-#e2)/POWER(1-#e2*#sinLat*#sinLat, 1.5)
DECLARE #eta2 real = #nu/#rho-1
DECLARE #tanLat real = tan(#lat)
DECLARE #tan2lat real = #tanLat*#tanLat
DECLARE #tan4lat real = #tan2lat*#tan2lat
DECLARE #tan6lat real = #tan4lat*#tan2lat
DECLARE #secLat real = 1/#cosLat
DECLARE #nu3 real = #nu*#nu*#nu
DECLARE #nu5 real = #nu3*#nu*#nu
DECLARE #nu7 real = #nu5*#nu*#nu
DECLARE #VII real = #tanLat/(2*#rho*#nu)
DECLARE #VIII real = #tanLat/(24*#rho*#nu3)*(5+3*#tan2lat+#eta2-9*#tan2lat*#eta2)
DECLARE #IX real = #tanLat/(720*#rho*#nu5)*(61+90*#tan2lat+45*#tan4lat)
DECLARE #X real = #secLat/#nu
DECLARE #XI real = #secLat/(6*#nu3)*(#nu/#rho+2*#tan2lat)
DECLARE #XII real = #secLat/(120*#nu5)*(5+28*#tan2lat+24*#tan4lat)
DECLARE #XIIA real = #secLat/(5040*#nu7)*(61+662*#tan2lat+1320*#tan4lat+720*#tan6lat)
DECLARE #dE real = (#east-#E0)
DECLARE #dE2 real = #dE*#dE
DECLARE #dE3 real = #dE2*#dE
DECLARE #dE4 real = #dE2*#dE2,
#dE5 real = #dE3*#dE2
DECLARE #dE6 real = #dE4*#dE2,
#dE7 real = #dE5*#dE2
SET #lat = #lat - #VII*#dE2 + #VIII*#dE4 - #IX*#dE6
DECLARE #lon real = #lon0 + #X*#dE - #XI*#dE3 + #XII*#dE5 - #XIIA*#dE7
SELECT #lon, #lat
I've been struggling with this one for a while.
I had a lot of northing/easting points in OSGB36 that have to be converted on the fly on a regular basis.
Please note that the UDF below converts northings/eastings in OSGB36 (Ordnance Survey) projection to latitude/longitude in WGS84 projection so they can be used in Google Maps.
/****** Object: UserDefinedFunction [dbo].[NEtoLL] Script Date: 09/06/2012 17:06:39 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[NEtoLL] (#East INT, #North INT, #LatOrLng VARCHAR(3)) RETURNS FLOAT AS
BEGIN
--Author: Sandy Motteram
--Date: 06 September 2012
--UDF adapted from javascript at http://www.bdcc.co.uk/LatLngToOSGB.js
--found on page http://mapki.com/wiki/Tools:Snippets
--Instructions:
--Latitude and Longitude are calculated based on BOTH the easting and northing values from the OSGB36
--This UDF takes both easting and northing values in OSGB36 projection and you must specify if a latitude or longitude co-ordinate should be returned.
--IT first converts E/N values to lat and long in OSGB36 projection, then converts those values to lat/lng in WGS84 projection
--Sample values below
--DECLARE #East INT, #North INT, #LatOrLng VARCHAR(3)
--SELECT #East = 529000, #North = 183650 --that combo should be the corner of Camden High St and Delancey St
DECLARE #Pi FLOAT
, #K0 FLOAT
, #OriginLat FLOAT
, #OriginLong FLOAT
, #OriginX FLOAT
, #OriginY FLOAT
, #a FLOAT
, #b FLOAT
, #e2 FLOAT
, #ex FLOAT
, #n1 FLOAT
, #n2 FLOAT
, #n3 FLOAT
, #OriginNorthings FLOAT
, #lat FLOAT
, #lon FLOAT
, #Northing FLOAT
, #Easting FLOAT
SELECT #Pi = 3.14159265358979323846
, #K0 = 0.9996012717 -- grid scale factor on central meridean
, #OriginLat = 49.0
, #OriginLong = -2.0
, #OriginX = 400000 -- 400 kM
, #OriginY = -100000 -- 100 kM
, #a = 6377563.396 -- Airy Spheroid
, #b = 6356256.910
/* , #e2
, #ex
, #n1
, #n2
, #n3
, #OriginNorthings*/
-- compute interim values
SELECT #a = #a * #K0
, #b = #b * #K0
SET #n1 = (#a - #b) / (#a + #b)
SET #n2 = #n1 * #n1
SET #n3 = #n2 * #n1
SET #lat = #OriginLat * #Pi / 180.0 -- to radians
SELECT #e2 = (#a * #a - #b * #b) / (#a * #a) -- first eccentricity
, #ex = (#a * #a - #b * #b) / (#b * #b) -- second eccentricity
SET #OriginNorthings = #b * #lat + #b * (#n1 * (1.0 + 5.0 * #n1 * (1.0 + #n1) / 4.0) * #lat
- 3.0 * #n1 * (1.0 + #n1 * (1.0 + 7.0 * #n1 / 8.0)) * SIN(#lat) * COS(#lat)
+ (15.0 * #n1 * (#n1 + #n2) / 8.0) * SIN(2.0 * #lat) * COS(2.0 * #lat)
- (35.0 * #n3 / 24.0) * SIN(3.0 * #lat) * COS(3.0 * #lat))
SELECT #northing = #north - #OriginY
, #easting = #east - #OriginX
DECLARE #nu FLOAT
, #phid FLOAT
, #phid2 FLOAT
, #t2 FLOAT
, #t FLOAT
, #q2 FLOAT
, #c FLOAT
, #s FLOAT
, #nphid FLOAT
, #dnphid FLOAT
, #nu2 FLOAT
, #nudivrho FLOAT
, #invnurho FLOAT
, #rho FLOAT
, #eta2 FLOAT
/* Evaluate M term: latitude of the northing on the centre meridian */
SET #northing = #northing + #OriginNorthings
SET #phid = #northing / (#b*(1.0 + #n1 + 5.0 * (#n2 + #n3) / 4.0)) - 1.0
SET #phid2 = #phid + 1.0
WHILE (ABS(#phid2 - #phid) > 0.000001)
BEGIN
SET #phid = #phid2;
SET #nphid = #b * #phid + #b * (#n1 * (1.0 + 5.0 * #n1 * (1.0 + #n1) / 4.0) * #phid
- 3.0 * #n1 * (1.0 + #n1 * (1.0 + 7.0 * #n1 / 8.0)) * SIN(#phid) * COS(#phid)
+ (15.0 * #n1 * (#n1 + #n2) / 8.0) * SIN(2.0 * #phid) * COS(2.0 * #phid)
- (35.0 * #n3 / 24.0) * SIN(3.0 * #phid) * COS(3.0 * #phid))
SET #dnphid = #b * ((1.0 + #n1 + 5.0 * (#n2 + #n3) / 4.0) - 3.0 * (#n1 + #n2 + 7.0 * #n3 / 8.0) * COS(2.0 * #phid)
+ (15.0 * (#n2 + #n3) / 4.0) * COS(4 * #phid) - (35.0 * #n3 / 8.0) * COS(6.0 * #phid))
SET #phid2 = #phid - (#nphid - #northing) / #dnphid
END
SELECT #c = COS(#phid)
, #s = SIN(#phid)
, #t = TAN(#phid)
SELECT #t2 = #t * #t
, #q2 = #easting * #easting
SET #nu2 = (#a * #a) / (1.0 - #e2 * #s * #s)
SET #nu = SQRT(#nu2)
SET #nudivrho = #a * #a * #c * #c / (#b * #b) - #c * #c + 1.0
SET #eta2 = #nudivrho - 1
SET #rho = #nu / #nudivrho;
SET #invnurho = ((1.0 - #e2 * #s * #s) * (1.0 - #e2 * #s * #s)) / (#a * #a * (1.0 - #e2))
SET #lat = #phid - #t * #q2 * #invnurho / 2.0 + (#q2 * #q2 * (#t / (24 * #rho * #nu2 * #nu) * (5 + (3 * #t2) + #eta2 - (9 * #t2 * #eta2))))
SET #lon = (#easting / (#c * #nu))
- (#easting * #q2 * ((#nudivrho + 2.0 * #t2) / (6.0 * #nu2)) / (#c * #nu))
+ (#q2 * #q2 * #easting * (5 + (28 * #t2) + (24 * #t2 * #t2)) / (120 * #nu2 * #nu2 * #nu * #c))
SELECT #lat = #lat * 180.0 / #Pi
, #lon = #lon * 180.0 / #Pi + #OriginLong
--Now convert the lat and long from OSGB36 to WGS84
DECLARE #OGlat FLOAT
, #OGlon FLOAT
, #height FLOAT
SELECT #OGlat = #lat
, #OGlon = #lon
, #height = 24 --London's mean height above sea level is 24 metres. Adjust for other locations.
DECLARE #deg2rad FLOAT
, #rad2deg FLOAT
, #radOGlat FLOAT
, #radOGlon FLOAT
SELECT #deg2rad = #Pi / 180
, #rad2deg = 180 / #Pi
--first off convert to radians
SELECT #radOGlat = #OGlat * #deg2rad
, #radOGlon = #OGlon * #deg2rad
--these are the values for WGS84(GRS80) to OSGB36(Airy)
DECLARE #a2 FLOAT
, #h FLOAT
, #xp FLOAT
, #yp FLOAT
, #zp FLOAT
, #xr FLOAT
, #yr FLOAT
, #zr FLOAT
, #sf FLOAT
, #e FLOAT
, #v FLOAT
, #x FLOAT
, #y FLOAT
, #z FLOAT
, #xrot FLOAT
, #yrot FLOAT
, #zrot FLOAT
, #hx FLOAT
, #hy FLOAT
, #hz FLOAT
, #newLon FLOAT
, #newLat FLOAT
, #p FLOAT
, #errvalue FLOAT
, #lat0 FLOAT
SELECT #a2 = 6378137 -- WGS84_AXIS
, #e2 = 0.00669438037928458 -- WGS84_ECCENTRIC
, #h = #height -- height above datum (from $GPGGA sentence)
, #a = 6377563.396 -- OSGB_AXIS
, #e = 0.0066705397616 -- OSGB_ECCENTRIC
, #xp = 446.448
, #yp = -125.157
, #zp = 542.06
, #xr = 0.1502
, #yr = 0.247
, #zr = 0.8421
, #s = -20.4894
-- convert to cartesian; lat, lon are in radians
SET #sf = #s * 0.000001
SET #v = #a / (sqrt(1 - (#e * (SIN(#radOGlat) * SIN(#radOGlat)))))
SET #x = (#v + #h) * COS(#radOGlat) * COS(#radOGlon)
SET #y = (#v + #h) * COS(#radOGlat) * SIN(#radOGlon)
SET #z = ((1 - #e) * #v + #h) * SIN(#radOGlat)
-- transform cartesian
SET #xrot = (#xr / 3600) * #deg2rad
SET #yrot = (#yr / 3600) * #deg2rad
SET #zrot = (#zr / 3600) * #deg2rad
SET #hx = #x + (#x * #sf) - (#y * #zrot) + (#z * #yrot) + #xp
SET #hy = (#x * #zrot) + #y + (#y * #sf) - (#z * #xrot) + #yp
SET #hz = (-1 * #x * #yrot) + (#y * #xrot) + #z + (#z * #sf) + #zp
-- Convert back to lat, lon
SET #newLon = ATAN(#hy / #hx)
SET #p = SQRT((#hx * #hx) + (#hy * #hy))
SET #newLat = ATAN(#hz / (#p * (1 - #e2)))
SET #v = #a2 / (SQRT(1 - #e2 * (SIN(#newLat) * SIN(#newLat))))
SET #errvalue = 1.0;
SET #lat0 = 0
WHILE (#errvalue > 0.001)
BEGIN
SET #lat0 = ATAN((#hz + #e2 * #v * SIN(#newLat)) / #p)
SET #errvalue = ABS(#lat0 - #newLat)
SET #newLat = #lat0
END
--convert back to degrees
SET #newLat = #newLat * #rad2deg
SET #newLon = #newLon * #rad2deg
DECLARE #ReturnMe FLOAT
SET #ReturnMe = 0
IF #LatOrLng = 'Lat'
SET #ReturnMe = #newLat
IF #LatOrLng = 'Lng'
SET #ReturnMe = #newLon
RETURN #ReturnMe
END
GO
I ended up using the following javascript functions to convert the values. I know it's not a SQL solution but it did the job for me.
function OSGridToLatLong(E, N) {
var a = 6377563.396, b = 6356256.910; // Airy 1830 major & minor semi-axes
var F0 = 0.9996012717; // NatGrid scale factor on central meridian
var lat0 = 49*Math.PI/180, lon0 = -2*Math.PI/180; // NatGrid true origin
var N0 = -100000, E0 = 400000; // northing & easting of true origin, metres
var e2 = 1 - (b*b)/(a*a); // eccentricity squared
var n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
var lat=lat0, M=0;
do {
lat = (N-N0-M)/(a*F0) + lat;
var Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
var Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(lat-lat0) * Math.cos(lat+lat0);
var Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(lat-lat0)) * Math.cos(2*(lat+lat0));
var Md = (35/24)*n3 * Math.sin(3*(lat-lat0)) * Math.cos(3*(lat+lat0));
M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
} while (N-N0-M >= 0.00001); // ie until < 0.01mm
var cosLat = Math.cos(lat), sinLat = Math.sin(lat);
var nu = a*F0/Math.sqrt(1-e2*sinLat*sinLat); // transverse radius of curvature
var rho = a*F0*(1-e2)/Math.pow(1-e2*sinLat*sinLat, 1.5); // meridional radius of curvature
var eta2 = nu/rho-1;
var tanLat = Math.tan(lat);
var tan2lat = tanLat*tanLat, tan4lat = tan2lat*tan2lat, tan6lat = tan4lat*tan2lat;
var secLat = 1/cosLat;
var nu3 = nu*nu*nu, nu5 = nu3*nu*nu, nu7 = nu5*nu*nu;
var VII = tanLat/(2*rho*nu);
var VIII = tanLat/(24*rho*nu3)*(5+3*tan2lat+eta2-9*tan2lat*eta2);
var IX = tanLat/(720*rho*nu5)*(61+90*tan2lat+45*tan4lat);
var X = secLat/nu;
var XI = secLat/(6*nu3)*(nu/rho+2*tan2lat);
var XII = secLat/(120*nu5)*(5+28*tan2lat+24*tan4lat);
var XIIA = secLat/(5040*nu7)*(61+662*tan2lat+1320*tan4lat+720*tan6lat);
var dE = (E-E0), dE2 = dE*dE, dE3 = dE2*dE, dE4 = dE2*dE2, dE5 = dE3*dE2, dE6 = dE4*dE2, dE7 = dE5*dE2;
lat = lat - VII*dE2 + VIII*dE4 - IX*dE6;
var lon = lon0 + X*dE - XI*dE3 + XII*dE5 - XIIA*dE7;
return {
longitude: lon.toDeg(),
latitude: lat.toDeg()
};
}
Number.prototype.toRad = function() { // convert degrees to radians
return this * Math.PI / 180;
}
Number.prototype.toDeg = function() { // convert radians to degrees (signed)
return this * 180 / Math.PI;
}
Number.prototype.padLZ = function(w) {
var n = this.toString();
for (var i=0; i<w-n.length; i++) n = '0' + n;
return n;
}
I needed the same function, and javascript made it difficult to interact with the DB. I have converted your JS to PHP and this could be more useful when updating your database - ie: query table, loop through result set, call function, update table.
function OSGridToLatLong($E, $N) {
$a = 6377563.396;
$b = 6356256.910; // Airy 1830 major & minor semi-axes
$F0 = 0.9996012717; // NatGrid scale factor on central meridian
$lat0 = 49*M_PI/180;
$lon0 = -2*M_PI/180; // NatGrid true origin
$N0 = -100000;
$E0 = 400000; // northing & easting of true origin, metres
$e2 = 1 - ($b*$b)/($a*$a); // eccentricity squared
$n = ($a-$b)/($a+$b);
$n2 = $n*$n;
$n3 = $n*$n*$n;
$lat=$lat0;
$M=0;
do {
$lat = ($N-$N0-$M)/($a*$F0) + $lat;
$Ma = (1 + $n + (5/4)*$n2 + (5/4)*$n3) * ($lat-$lat0);
$Mb = (3*$n + 3*$n*$n + (21/8)*$n3) * sin($lat-$lat0) * cos($lat+$lat0);
$Mc = ((15/8)*$n2 + (15/8)*$n3) * sin(2*($lat-$lat0)) * cos(2*($lat+$lat0));
$Md = (35/24)*$n3 * sin(3*($lat-$lat0)) * cos(3*($lat+$lat0));
$M = $b * $F0 * ($Ma - $Mb + $Mc - $Md); // meridional arc
} while ($N-$N0-$M >= 0.00001); // ie until < 0.01mm
$cosLat = cos($lat);
$sinLat = sin($lat);
$nu = $a*$F0/sqrt(1-$e2*$sinLat*$sinLat); // transverse radius of curvature
$rho = $a*$F0*(1-$e2)/pow(1-$e2*$sinLat*$sinLat, 1.5); // meridional radius of curvature
$eta2 = $nu/$rho-1;
$tanLat = tan($lat);
$tan2lat = $tanLat*$tanLat;
$tan4lat = $tan2lat*$tan2lat;
$tan6lat = $tan4lat*$tan2lat;
$secLat = 1/$cosLat;
$nu3 = $nu*$nu*$nu;
$nu5 = $nu3*$nu*$nu;
$nu7 = $nu5*$nu*$nu;
$VII = $tanLat/(2*$rho*$nu);
$VIII = $tanLat/(24*$rho*$nu3)*(5+3*$tan2lat+$eta2-9*$tan2lat*$eta2);
$IX = $tanLat/(720*$rho*$nu5)*(61+90*$tan2lat+45*$tan4lat);
$X = $secLat/$nu;
$XI = $secLat/(6*$nu3)*($nu/$rho+2*$tan2lat);
$XII = $secLat/(120*$nu5)*(5+28*$tan2lat+24*$tan4lat);
$XIIA = $secLat/(5040*$nu7)*(61+662*$tan2lat+1320*$tan4lat+720*$tan6lat);
$dE = ($E-$E0);
$dE2 = $dE*$dE;
$dE3 = $dE2*$dE;
$dE4 = $dE2*$dE2;
$dE5 = $dE3*$dE2;
$dE6 = $dE4*$dE2;
$dE7 = $dE5*$dE2;
$lat = $lat - $VII*$dE2 + $VIII*$dE4 - $IX*$dE6;
$lon = $lon0 + $X*$dE - $XI*$dE3 + $XII*$dE5 - $XIIA*$dE7;
return array(
'longitude' => $lon * 180 / M_PI,
'latitude' => $lat * 180 / M_PI
);
}
If anyone's interested in non-SQL solution I strongly recommend using this http://www.howtocreate.co.uk/php/gridref.php PHP/JavaScript class.
One important thing to mention here is the library supports Helmert transformation.
PHP
$grutoolbox = Grid_Ref_Utils::toolbox();
$source_coords = Array(54.607720,-6.411990);
//get the ellipsoids that will be used
$Airy_1830 = $grutoolbox->get_ellipsoid('Airy_1830');
$WGS84 = $grutoolbox->get_ellipsoid('WGS84');
$Airy_1830_mod = $grutoolbox->get_ellipsoid('Airy_1830_mod');
//get the transform parameters that will be used
$UK_to_GPS = $grutoolbox->get_transformation('OSGB36_to_WGS84');
$GPS_to_Ireland = $grutoolbox->get_transformation('WGS84_to_Ireland65');
//convert to GPS coordinates
$gps_coords = $grutoolbox->Helmert_transform($source_coords,$Airy_1830,$UK_to_GPS,$WGS84);
//convert to destination coordinates
print $grutoolbox->Helmert_transform($source_coords,$WGS84,$GPS_to_Ireland,$Airy_1830_mod,$grutoolbox->HTML);
JavaScript
var grutoolbox = gridRefUtilsToolbox();
var sourceCoords = [54.607720,-6.411990];
//get the ellipsoids that will be used
var Airy1830 = grutoolbox.getEllipsoid('Airy_1830');
var WGS84 = grutoolbox.getEllipsoid('WGS84');
var Airy1830Mod = grutoolbox.getEllipsoid('Airy_1830_mod');
//get the transform parameters that will be used
var UKToGPS = grutoolbox.getTransformation('OSGB36_to_WGS84');
var GPSToIreland = grutoolbox.getTransformation('WGS84_to_Ireland65');
//convert to GPS coordinates
var gpsCoords = grutoolbox.HelmertTransform(sourceCoords,Airy1830,UKToGPS,WGS84);
//convert to destination coordinates
element.innerHTML = grutoolbox.HelmertTransform(sourceCoords,WGS84,GPSToIreland,Airy1830Mod,grutoolbox.HTML);
I have developed a library in .NET to be called from transact sql Converts WGS84/UTM coordinates to Latitude and Longitude
It does just the opposite, but as it uses CoordinateSharp you can download the code and change it easily to convert from lat/long to wgs84 instead.
You can download it from github:
https://github.com/j-v-garcia/UTM2LATITUDE
usage:
SELECT dbo.UTM2LATITUDE(723399.51,4373328.5,'S',30) AS Latitude, dbo.UTM2LONGITUDE(723399.51,4373328.5,'S',30) AS Longitude
result:
39,4805657453054 -0,402592727245112
<param name="XUTM">pos UTM X</param>
<param name="YUTM">pos UTM Y</param>
<param name="LatBand">Latitude band grid zone designation letter (see http://www.dmap.co.uk/utmworld.htm) </param>
<param name="LongBand">Longitude band grid zone designation number (see http://www.dmap.co.uk/utmworld.htm) </param>