Sybase iAnywhere SQL AAnywhere Mobile and Embedded Database

Rows and Columns


SQL Anywhere perspective on technology

header image

SQL Anywhere - First Business Database to Receive China’s Security Certificate

By Biff on May 14th, 2009

It has been a long process getting our software in shape and passing through all of the various hurdles that would allow East Port to build and offer secure business solutions based on SQL Anywhere, called E-Secure Anywhere. It has finally happened:
“E-Secure Anywhere can now be widely deployed in environments that require special security needs, such as government agencies.”

See the full press release here:
Sybase, East Port Joint Product Becomes First Business Database to Receive China’s Security Certificate

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • description
  • LinkedIn
  • NewsVine
  • Print this article!
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis
  • Yahoo! Buzz

→ No CommentsPosted in: Uncategorized

Formatting a Tag Cloud

By Biff on April 27th, 2009

Previously, I talked about creating a tag cloud, aggregating tag information from multiple blogs.
In order to create/display the tag cloud on the Sybase blog homepage, we had to determine how to integrate the data and the CSS that was generated to display the tag cloud. After some discussion, we decided to feed the tag cloud data to the page using javascript.
The web team generated 5 classes to define display characteristics for tags based on usage. The format they were looking for for each tag was basically as follows:

<a class="tagclass1">
    tag
</a>

This way, they could create and manage “tagclass1″ to display the tag formatted to fit the page where the tagcloud appeared.
Since there were 5 tag classes, I had to loop through my result set of tags and counts, and classify each one.
The following php function demonstrates how this was done:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function get_tag_cloud() {
  global $sample;
 
  // Pull in tag data
  $tags = get_tag_data();
  $numtags = count($tags); //How many tags are there?
 
  $arrcount = $tags;
  asort( $arrcount ); //Copy the array (which was sorted by tag), and sort by count.
 
  //Start the HTML building process to display our tags and link to the rss for the tag
  $cloud_html = '';
  $cloud_tags = array(); // create an array to hold tag code
  $i = 0;
 
  //Ranks will be evenly spread across all tags - not perfect, but should be ok.
  foreach ($arrcount as $tag => $count) {
    $i++;
    //Since we are going through the tags in order by the number of occurrences, we simply divide 
    // the array into 5 chunks, and assign a tagclass to each
    $tag_class = "tagrank" . ceil(5*$i/$numtags);
 
    //Notice that we also include a link to an RSS page for each tag, and we add the tag count
    // information to appear when the cursor hovers over the tag
    // Also notice we index the cloud_tags array by tag - so that we can later resort the result
    // alphabetically by tagname
    $cloud_tags[$tag] = '<a class="'.$tag_class.'" href="http://blogs.sybase.com/bytag.php?tag=' . $tag 
      . '" title="' . $tag  . ' tag count is ' . $count . '">' 
      . htmlspecialchars(stripslashes($tag)) . '</a>';
  }
  //Sort the resulting array of classes alphabetically by the tag
  ksort( $cloud_tags );
  $cloud_html = join("&nbsp", $cloud_tags);
 
  return $cloud_html;
}

To get the tag cloud string back to the browser so it could be pulled in from a variety of pages on the website, I used javascript. The final generated output from the tagcloud.php script is simply:

header("Content-type: application/x-javascript"); // we are using javascript
$content = "document.write( '<html xmlns=\"http://www.w3.org/1999/xhtml\" >";
$content = $content . get_tag_cloud(); //write the generated tagcloud information 
$content = $content . "</html>')";
echo $content;

So the browser receives the document.write(…) and writes out the tag cloud data, applying the css for the tag classes from whatever page is including it on the Sybase site.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • description
  • LinkedIn
  • NewsVine
  • Print this article!
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis
  • Yahoo! Buzz

→ No CommentsPosted in: Uncategorized

Creating a WordPress multi-blog Tag Cloud

By Biff on April 20th, 2009

Now that the new blogs have been up for a while and everything seems stable, the Sybase blogging homepage is undergoing some changes. One of the things that was recently added is a tag cloud, which allows visitors to get a quick picture of the topics the Sybase bloggers are blogging about, and even click through to view the list of articles associated with a specific tag. There is no mandated tag set, so the tag cloud is quite large on the page in its full view, which is why there is a shortened view available by default which gives a sampling of the tags.

While the visual representation of the tag cloud was done by the Sybase Web Marketing team, the actual data comes from the blog content. Since I had already experimented and figured out how to generate RSS from the database, the only thing I really had to figure out was how to get the tag information out to the tag cloud.

I started by determining where the tag information was stored in the database:

SELECT name AS tag, "count";
FROM terms t, term_taxonomy tt
WHERE t.term_id = tt.term_id AND taxonomy = 'post_tag' AND "count" > 0;

This gave me the tag list for a single user. Since I created each blogger with their own set of tables in the same SQL Anywhere database, I simply had to union this query together once for each blogger, changing the table names each time. At some point in the future, I may make this more dynamic so it automatically picks up new bloggers, but for now it works fine:

SELECT name AS tag, count 
FROM wp_terms t, wp_term_taxonomy tt
WHERE t.term_id = tt.term_id AND taxonomy = 'post_tag' AND "count" > 0;
UNION ALL
SELECT name AS tag, count 
FROM wpuser1_terms t, wpuser1_term_taxonomy tt 
WHERE t.term_id = tt.term_id AND taxonomy = 'post_tag' AND "count" > 0;
...

As I said above, there is no standard tag list for the bloggers. This meant there was a risk of different bloggers using the same tag with different case and the tag would show up multiple times. In addition, I wanted to eliminate any tag that was only used once in total across all blogs, and return the combined count for each individual tag. I did this using a group by and having; clause. My final query looked something like this:

SELECT LOWER(tag) AS tag, sum(count) AS count 
FROM (SELECT name AS tag, count 
	FROM wp_terms t, wp_term_taxonomy tt
	WHERE t.term_id = tt.term_id AND taxonomy = 'post_tag' AND "count" > 0;
      UNION ALL
      SELECT name AS tag, count 
	FROM wpuser1_terms t, wpuser1_term_taxonomy tt 
	WHERE t.term_id = tt.term_id AND taxonomy = 'post_tag' AND "count" > 0;
      UNION ALL
      ...
     ) AS T1
GROUP BY tag 
HAVING count >  1 
ORDER BY SORTKEY(tag, 'nocase');

Notice the result set is sorted by tag. This made it easier to classify the tags into various categories for display purposes.

Now that I had the data, I had to put it into a format that made it easy/useful for the Sybase Web Team to display. I’ll talk about that in my next post.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • description
  • LinkedIn
  • NewsVine
  • Print this article!
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis
  • Yahoo! Buzz

→ 1 CommentPosted in: SQL Anywhere · WordPress

GPS - How Does It Work? - Part 3

By Biff on April 13th, 2009

In my first post in the series, I covered how one might gain access to a GPS signal and interpret it. Post 2 covered displaying the data and storing it in the database.
Now that we have some data collected and stored in the database, we can do some other things to it.
For example, here is a SQL function which will calculate the distance between 2 points on a sphere - I know the earth isn’t a sphere, but this formula is much easier to use and is close enough for my purposes.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE FUNCTION "DBA"."HaversineDistance"( startinglatitude double, startinglongitude double, endinglatitude double, endinglongitude double )
returns numeric
BEGIN
	declare rEarth double ;
	declare deltaLat double;
	declare deltaLong double;
	declare a double;
	declare c double;
	declare dist double;
	declare degreesToRadians double;
 
	-- Haversine formula
	SET degreesToRadians = 0.017453292519943295769236907684886;
	SET rEarth = 6372.798; --Earth's radius - 6371km, 6372797.560856m
	SET deltaLat = (endinglatitude - startinglatitude) * degreesToRadians;
	SET deltaLong = (endinglongitude - startinglongitude) * degreesToRadians;
 
	SET a = power( sin(deltaLat/2), 2 ) + cos(startinglatitude*degreesToRadians) * cos(endinglatitude*degreesToRadians) * power(sin(deltaLong/2), 2);
	SET c = 2*atan2( sqrt(a), sqrt(1-a) );
	SET dist = rEarth * c;
	RETURN dist;
END;

And here is a cool function which uses some code from the project “Google Earth KML API for .NET” on SourceForge. it generates a simple KML document mapping out the coordinates in the database as a path. The generated document can be opened in Google Earth to give a display of the path

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
private void btnGenerateKML_Click(object sender, EventArgs e) {
    //Take co-ordinate information from database and generate a KML file
    geDocument doc;
    gePlacemark pmline;
    gePlacemark pmstartpoint;
    gePlacemark pmendpoint;
    geCoordinates coords;
    gePoint startpoint;
    gePoint endpoint;
    geKML KML;
    Int16 hrs;
    Decimal mins;
    Decimal latitude = 0;
    Decimal longitude = 0;
    String hemisphere;
    DateTime starttime = Convert.ToDateTime("1900/01/01");
    DateTime endtime = Convert.ToDateTime("1900/01/01");
    Boolean started = false;
    geLineString path;
    List<geCoordinates> path_coords;
 
    //Create the root kml document
    doc = new geDocument();
    doc.Name = "KML Root document";
 
    //Create a placemark for the document
    pmline = new gePlacemark();
    pmstartpoint = new gePlacemark();
    pmendpoint = new gePlacemark();
 
    path_coords = new List<geCoordinates>();
 
    //Get Co-ordinates from database
    m_rsPositions = m_cmdGetPositionInfo.ExecuteReader();
    while (m_rsPositions.Read()) {
        /* 
         * Latitude is measured from the equator, with positive values going north and negative values going south. 
         * Longitude is measured from the Prime Meridian with positive values going east and negative values going west.
         * There are 60 seconds in 1 minute and 60 minutes in 1 degree. So, for example:
         * 65:45:36 south latitude converts to
         * -(65 degrees 
         * + (45 minutes * (1 degree/60 minutes)) 
         * + (36 seconds * (1 minute/60 seconds) * (1 degree/60 minutes))) 
         * = -65.76 degrees latitude 
         */
        hrs = m_rsPositions.GetInt16(0);
        mins = m_rsPositions.GetDecimal(1);
        hemisphere = m_rsPositions.GetString(2);
        if( hemisphere.Equals("S") ) {
            latitude = -(hrs + (mins / 60)); // Convert.ToDecimal(hrs.ToString() + mins.ToString());
        }else {
            latitude = hrs + (mins / 60); // Convert.ToDecimal(hrs.ToString() + mins.ToString());
        }
 
        hrs = m_rsPositions.GetInt16(3);
        mins = m_rsPositions.GetDecimal(4);
        hemisphere = m_rsPositions.GetString(5);
        if (hemisphere.Equals("W")) {
            longitude = -(hrs + (mins / 60)); //Convert.ToDecimal(hrs.ToString() + mins.ToString());
        } else {
            longitude = hrs + (mins / 60); //Convert.ToDecimal(hrs.ToString() + mins.ToString());
        }
        if (!started) {
            starttime = m_rsPositions.GetDateTime(6);
            //Add the starting point
            coords = new geCoordinates(new geAngle90(Convert.ToDouble(latitude)), new geAngle180(Convert.ToDouble(longitude)));
            startpoint = new gePoint(coords);
            pmstartpoint.Geometry = startpoint;
            pmstartpoint.Name = "Here is the start point";
            pmstartpoint.Snippet = "A snippet for the start point would go here";
            pmstartpoint.Description = "Path starting point recorded at " + starttime.ToString();
            doc.Features.Add(pmstartpoint);
            started = true;
        }
        endtime = m_rsPositions.GetDateTime(6);
        path_coords.Add(new geCoordinates(new geAngle90(Convert.ToDouble(latitude)), new geAngle180(Convert.ToDouble(longitude))));
    }
    if (!started) {
        MessageBox.Show("No GPS data stored for user " + m_iduser.ToString(), "No data found");
        return;
    }
 
    //Add the path
    path = new geLineString(path_coords);
    pmline.Geometry = path;
    pmline.Name = "Here is a test path";
    pmline.Snippet = "A snippet would go here";
    pmline.Description = "Path recorded from " + starttime.ToString() + " to " + endtime.ToString();
    doc.Features.Add(pmline);
 
    //Add the end point
    coords = new geCoordinates(new geAngle90(Convert.ToDouble(latitude)), new geAngle180(Convert.ToDouble(longitude)));
    endpoint = new gePoint(coords);
    pmendpoint.Geometry = endpoint;
    pmendpoint.Name = "Here is the end point";
    pmendpoint.Snippet = "A snippet for the end point would go here";
    pmendpoint.Description = "Path ending point recorded at " + endtime.ToString();
    doc.Features.Add(pmendpoint);
 
    //Generate the actual KML
    KML = new geKML(doc);
 
    //Write the resulting KML document to a file
    File.WriteAllBytes("c:\temp\testoutput.kml", KML.ToKML());
}
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • description
  • LinkedIn
  • NewsVine
  • Print this article!
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis
  • Yahoo! Buzz

→ No CommentsPosted in: Development · SQL Anywhere · Spatial

GPS - How Does It Work? Part 2

By Biff on April 6th, 2009

In my previous post I explained how to receive and parse a GPS signal from a GPS device.

In order to use the signal parser, I created a (very) simple GUI to parse and display the GPS information. I dropped a multi-line edit on the form, declared some delegate functions to act as callbacks for the parsing process, and then created the parse function (once again from the CodeProject article I mentioned in my previous post). The following code is incomplete, but demonstrates what I did.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
public partial class Form1 : Form {
  private COMreader m_reader;
  private NmeaParser m_GPSInterpreter;
  private IDbConnection m_connection;
  private IDbCommand m_cmdInsertPosition;
  private IDbCommand m_cmdGetPositionInfo;
  private IDataReader m_rsPositions;
  private String m_RDBMS = "SQL Anywhere";
  private Int32 m_iduser = 1;
  private String m_lastlatitude;
  private String m_lastlongitude;
 
  public Form1() {
      InitializeComponent();
 
      //Allow other threads to affect the form GUI in a non-threadsafe way.
      //See http://msdn2.microsoft.com/en-us/library/system.windows.forms.control.checkforillegalcrossthreadcalls(VS.90).aspx
      // for the thread-safe way to make cross thread calls.
      // which is not really necessary in this demo.
      CheckForIllegalCrossThreadCalls = false;
 
      m_reader = new COMreader("COM18");
      m_GPSInterpreter = new NmeaParser();
      m_reader.dataReceived += new COMreader.DataReceivedEventHandler(Parse);
      m_GPSInterpreter.PositionReceived += new NmeaParser.PositionReceivedEventHandler(ShowPosition);
      m_GPSInterpreter.DateTimeChanged += new NmeaParser.DateTimeChangeEventHandler(ShowDateTime);
      m_GPSInterpreter.BearingReceived += new NmeaParser.BearingReceivedEventHandler(ShowBearing);
      m_GPSInterpreter.SpeedReceived += new NmeaParser.SpeedReceivedEventHandler(ShowSpeed);
      m_GPSInterpreter.SpeedLimitReached += new NmeaParser.SpeedLimitReachedEventHandler(ShowSpeedWarning);
      m_GPSInterpreter.FixObtained += new NmeaParser.FixObtainedEventHandler(ShowSatelliteFix);
      m_GPSInterpreter.FixLost += new NmeaParser.FixLostEventHandler(ShowSatelliteFixLost);
      m_GPSInterpreter.SatelliteReceived += new NmeaParser.SatelliteReceivedEventHandler(ShowSatelliteInfo);
 
      m_lastlongitude = "";
      m_lastlatitude = "";
  }
 
  // Processes information from the GPS receiver
  public void Parse(String sentence) {
      String[] word;
      // Discard the sentence if its checksum does not match our calculated checksum
      if (!m_GPSInterpreter.IsValid(sentence))
      {
                MessageBox.Show("Invalid Sentence");
      }
      // Look at the first word to decide where to go next
      tbPositionInfo.AppendText(sentence + "
");
      word = m_GPSInterpreter.GetWords(sentence);
      if (word[0] == "$GPRMC") {
	   // A "Recommended Minimum" sentence was found!
          if (!m_GPSInterpreter.ParseGPRMC(sentence)) {
              MessageBox.Show("Unable to parse GPRMC message");                    
          }
      }else if (word[0] == "$GPGSV") {
          if( !m_GPSInterpreter.ParseGPGSV(sentence) ) {
              MessageBox.Show("Unable to parse GPGSV message");
          }
      } else {
          // Indicate that the sentence was not recognized
          //MessageBox.Show("Unrecognized GPS message: " + word[0]);
      }
  }
 
  //Turn on/off the GPS capture from the COM port
  private void btnCapture_Click(object sender, EventArgs e) {
      //If Capture is off, turn it on
      if( btnCapture.Text.Equals( "Capture On" ) ) {
          //Turn on GPS capture
          m_reader.start(false);
          btnCapture.Text = "Capture Off";
      } else {
          //Turn off GPS capture
          m_reader.stop();
          btnCapture.Text = "Capture On";            
      }
  }
 
  //When the parser receives a message containing info. we want to display, it calls this function
  private void ShowPosition(Int16 latitudehrs, Decimal latitudemins, String latitudehs,
                            Int16 longitudehrs, Decimal longitudemins, String longitudehs) {
      String Latitude;
      String Longitude;
      SAParameter parm;
 
      Latitude = latitudehrs + "° ";			  // Append hours
      Latitude = Latitude + latitudemins + "\"\"";	  // Append minutes
      Latitude = Latitude + latitudehs + "  ";    	  // Append the hemisphere
      Longitude = longitudehrs + "° ";			  // Append hours
      Longitude = Longitude + longitudemins + "\"\"";	  // Append minutes
      Longitude = Longitude + longitudehs;			  // Append the hemisphere
 
      tbPositionInfo.AppendText( "position latitude: " + Latitude + ", longitude: " + Longitude + "
" );
      m_lastlatitude = Latitude;
      m_lastlongitude = Longitude;
  }
 
  private void ShowDateTime( DateTime dateTime ) {
      tbPositionInfo.AppendText("Datetime message: " + dateTime.ToString() + "
");
  }
  private void ShowBearing(Double bearing) {
      tbPositionInfo.AppendText("Bearing: " + bearing.ToString() + "
");
  }
  private void ShowSpeed(Double speed) {
      tbPositionInfo.AppendText("Speed: " + speed.ToString() + "
");
  }
  private void ShowSpeedWarning()  {
      tbPositionInfo.AppendText("Speed limit reached" + "
");
  }
  private void ShowSatelliteFix() {
      tbPositionInfo.AppendText("Satellite fix obtained" + "
");
  }
  private void ShowSatelliteFixLost() {
      tbPositionInfo.AppendText("Satellite fix lost" + "
");
  }
  private void ShowSatelliteInfo(Int32 pseudoRandomCode, Int32 azimuth, Int32 elevation, Int32 signalToNoiseRatio) {
      tbPositionInfo.AppendText("Satellite location azimuth: " + azimuth.ToString() 
                                  + " elevation: " + elevation.ToString() 
                                  + " signal to noise: " + signalToNoiseRatio.ToString() + "
");
  }

Being able to display the information is all well and good, but storing it to the database was my real goal. Now that I had all of the information parsing and displaying nicely, this was not that hard. I just had to add a reference to the SQLAnywhere data provider and instead of display the data, insert it into the database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
public partial class Form1 : Form {
  private COMreader m_reader;
  private NmeaParser m_GPSInterpreter;
  private IDbConnection m_connection;
  private IDbCommand m_cmdInsertPosition;
  private IDbCommand m_cmdGetPositionInfo;
  private IDataReader m_rsPositions;
  private String m_RDBMS = "SQL Anywhere";
  private Int32 m_iduser = 1;
  private String m_lastlatitude;
  private String m_lastlongitude;
 
  public Form1() {
      InitializeComponent();
 
      //Allow other threads to affect the form GUI in a non-threadsafe way.
      //See http://msdn2.microsoft.com/en-us/library/system.windows.forms.control.checkforillegalcrossthreadcalls(VS.90).aspx
      // for the thread-safe way to make cross thread calls.
      // which is not really necessary in this demo.
      CheckForIllegalCrossThreadCalls = false;
 
      m_reader = new COMreader("COM18");
      m_GPSInterpreter = new NmeaParser();
      m_reader.dataReceived += new COMreader.DataReceivedEventHandler(Parse);
      m_GPSInterpreter.PositionReceived += new NmeaParser.PositionReceivedEventHandler(ShowPosition);
      m_GPSInterpreter.DateTimeChanged += new NmeaParser.DateTimeChangeEventHandler(ShowDateTime);
      m_GPSInterpreter.BearingReceived += new NmeaParser.BearingReceivedEventHandler(ShowBearing);
      m_GPSInterpreter.SpeedReceived += new NmeaParser.SpeedReceivedEventHandler(ShowSpeed);
      m_GPSInterpreter.SpeedLimitReached += new NmeaParser.SpeedLimitReachedEventHandler(ShowSpeedWarning);
      m_GPSInterpreter.FixObtained += new NmeaParser.FixObtainedEventHandler(ShowSatelliteFix);
      m_GPSInterpreter.FixLost += new NmeaParser.FixLostEventHandler(ShowSatelliteFixLost);
      m_GPSInterpreter.SatelliteReceived += new NmeaParser.SatelliteReceivedEventHandler(ShowSatelliteInfo);
 
      if (!GetConnection()) {
          MessageBox.Show( "No database provider specified", "Error" );
          Application.Exit();
      }
 
      m_lastlongitude = "";
      m_lastlatitude = "";
  }
 
  //Initialize the connection and a command
  Boolean GetConnection() {
      if (m_RDBMS.Equals("SQL Anywhere")) {
          m_connection = new SAConnection("uid=dba;pwd=sql;eng=spatial");
          try {
              m_connection.Open();
                    m_cmdGetPositionInfo = new SACommand( "SELECT latitudehours, latitudeminutes, latitudehemisphere, " +
                                            "longitudehours, longitudeminutes, longitudehemisphere, positiontime " +
                                            "FROM Position " +
                                            "WHERE idperson = ? " + 
                                            "ORDER BY positiontime", (SAConnection)m_connection );
              m_cmdInsertPosition = new SACommand("INSERT INTO Position( idPerson, latitudehours, latitudeminutes, latitudehemisphere, " +
                                      "longitudehours, longitudeminutes, longitudehemisphere ) " +
                                      "VALUES ( ?, ?, ?, ?, ?, ?, ? )", (SAConnection)m_connection);
              SAParameter parm = new SAParameter();
              parm.ParameterName = "idperson";
              parm.SADbType = SADbType.Integer;
              parm.SourceColumn = "idperson";
              parm.Value = m_iduser;  //Would get this somewhere else
              m_cmdInsertPosition.Parameters.Add(parm);
              m_cmdGetPositionInfo.Parameters.Add(parm);
 
              parm = new SAParameter();
              parm.ParameterName = "latitudehours";
              parm.SADbType = SADbType.SmallInt;
              parm.SourceColumn = "latitudehours";
              m_cmdInsertPosition.Parameters.Add(parm);
 
              parm = new SAParameter();
              parm.ParameterName = "latitudeminutes";
              parm.SADbType = SADbType.Decimal;
              parm.SourceColumn = "latitudeminutes";
              m_cmdInsertPosition.Parameters.Add(parm);
 
              parm = new SAParameter();
              parm.ParameterName = "latitudehemisphere";
              parm.SADbType = SADbType.VarChar;
              parm.SourceColumn = "latitudehemisphere";
              m_cmdInsertPosition.Parameters.Add(parm);
 
              parm = new SAParameter();
              parm.ParameterName = "longitudehours";
              parm.SADbType = SADbType.SmallInt;
              parm.SourceColumn = "longitudehours";
              m_cmdInsertPosition.Parameters.Add(parm);
 
              parm = new SAParameter();
              parm.ParameterName = "longitudeminutes";
              parm.SADbType = SADbType.Decimal;
              parm.SourceColumn = "longitudeminutes";
              m_cmdInsertPosition.Parameters.Add(parm);
 
              parm = new SAParameter();
              parm.ParameterName = "longitudehemisphere";
              parm.SADbType = SADbType.VarChar;
              parm.SourceColumn = "longitudehemisphere";
              m_cmdInsertPosition.Parameters.Add(parm);
          } catch (SAException ex) {
              MessageBox.Show(ex.Errors[0].Source + " : " + ex.Errors[0].Message + " (" + ex.Errors[0].NativeError.ToString() + ")",
                              "Failed to connect");
              Application.Exit();
          }
          return true;
      }
      return false;
  }
 
  // Processes information from the GPS receiver
  public void Parse(String sentence) {
      String[] word;
      // Discard the sentence if its checksum does not match our calculated checksum
      if (!m_GPSInterpreter.IsValid(sentence)) {
//                MessageBox.Show("Invalid Sentence");
      }
      // Look at the first word to decide where to go next
      tbPositionInfo.AppendText(sentence + "
");
      word = m_GPSInterpreter.GetWords(sentence);
      if (word[0] == "$GPRMC") {			   // A "Recommended Minimum" sentence was found!
          if (!m_GPSInterpreter.ParseGPRMC(sentence)) {
              MessageBox.Show("Unable to parse GPRMC message");                    
          }
      } else if (word[0] == "$GPGSV") {