Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Ionosondes Group Space Science and Technology Department CCLRC Rutherford Appleton Laboratory Chilton Didcot Oxfordshire OX11 0QX http://www.cclrc.ac.uk http://www.wdc.rl.ac.uk Lightning Detection System for Ionospheric Studies Nuffield Science Bursary Project 2004 Daniel Wilkins Supervisor: Dr Chris Davis CCLRC Rutherford Appleton Laboratory Table of Contents 1. Introduction............................................................................................3 i. The Ionosphere.......................................................................3 ii. The Research Project.............................................................3 iii. Project Brief.............................................................................4 2. Lightning Detection Systems................................................................5 i. Hardware Detectors................................................................5 ii. Software ..................................................................................7 3. Software Design....................................................................................9 i. Task of Software .....................................................................9 ii. Main Routines .........................................................................9 iii. Initial Design of Software......................................................11 iv. Problems Encountered with Software .................................11 v. Final Build..............................................................................12 4. Using the Data.....................................................................................14 i. Using the Data for Research................................................14 ii. Graphical Display of Data.....................................................15 5. Calibration............................................................................................17 6. Conclusions/Future of the Project ......................................................19 7. Appendices..........................................................................................20 i. Flow of Data through System...............................................21 ii. Software Data Acquisition System Flowchart .....................22 iii. C++ Code for Data Acquisition Tool ....................................23 iv. Acknowledgements ..............................................................31 2 1 Chapter Introduction The Ionosphere The ionosphere is a layer of ionised gas (produced by many things including the interaction of radiation, particularly in the ultraviolet part of the spectrum, from the sun with neutral atoms and molecules), between altitudes of approximately 80 km and 640 km. The ionisation is being continually generated by sunlight and destroyed through collisions with other ions. The lifetime of individual ions ranges from a fraction of a second to many hundreds of seconds depending on the atmospheric pressure at each height. The ionosphere affects the propagation of radio waves. As the waves interact with the ions and free electrons, they are partially refracted and reflected back to Earth when the frequency of the wave is equal to the resonant frequency of the gas; this phenomenon can have a great effect on radio communications as they alter the range and clarity of transmitted signals. This phenomenon can be used to make measurements of the ionosphere using an ionosonde which reflects radio waves off of the ionosphere and measures what is received on the ground and from these measurements can determine the height profile of atmospheric ionisation. The frequency of the radio waves reflected can be related to the electron concentration at that altitude using the formula: f = 8.98 [e − ] Where f is the frequency of the radio wave reflected (in MHz) and [e-] is the electron concentration (in electrons per m3). This makes it possible to build up a picture of ionisation against height. This ionosphere is generally divided into four distinct layers; D, E, F1 and F2 depending on the frequency of radio wave that is reflected or can penetrate the layers. The lowest frequencies are absorbed by the D layer (which does not always exist) as electrons take the wave energy and start to resonate but before the wave can be reradiated, the energy is dissipated by collisions with other gas particles due the higher pressure at the lower altitude of the D layer. Higher frequency waves can penetrate to the F layers before being reflected back to Earth. There is also the Es layer (sporadic E) which is a very dense, patchy layer of metal ions which sometimes appears at an altitude of around 100 km and reflects a lot of radio waves extending the range considerably of many transmitted signals. The Research Project A lot is still unknown about the factors that affect the composition of the ionosphere and how energy is transferred between the upper and lower parts of the atmosphere. This information would be useful as it would allow better prediction of the state of the ionosphere and to find how, if it is at all possible, energy is transferred between the upper and lower parts of the atmosphere. 3 This Nuffield Science Bursary project is to be the first step in a research project to find out exactly what affects the ionosphere, in particular the affect of electrical activity in the form of lightning strikes on the ionosphere, with particular focus on how electrical activity affects the composition of the ionosphere. Project Brief The Ionosondes group at CCLRC Rutherford Appleton Laboratory have been collecting Ionospheric data using the digital ionosonde on the Chilton site (and others, including the one at Port Stanley in the Falklands). In order to carry out this research, the group require a system to obtain data about lightning strikes in the vicinity of the Chilton ionosonde which must be able to obtain data about lightning strikes which can differentiate between strikes regarding the properties that could potentially cause changes in the ionosphere, including: • How far away the strike was • The type of lightning strike (cloud-to-ground, intra-cloud or sprite – a strike that travels up from the cloud to the ionosphere) • The polarity of the strike (the direction of electron flow) • The potential difference between the cloud and ground, or the ionosphere in the case of sprites (proportional to the intensity of the strike). The data about the lightning strikes is to be placed in the World Data Centre database for analysis. 4 2 Chapter Lightning Detection Systems Hardware Lightning Detection Systems A number of lightning detection systems are currently available on the market. Many of these work on the principle that lightning strikes generate VHF radio noise which can be detected in antenna coils within the detector. The direction of the strike is determined by two crossed coils within the detector and the distance is determined by the signal strength (which means they cannot be used to determine the intensity of strikes) as the signal is attenuated as it passes through the air. Two such systems are available from Boltek (http://www.boltek.com): Boltek® StormTracker™ This system consists of an antenna unit (black box) connected to a PCI card and is supplied with simple software to locate lightning strikes. The antenna unit can be installed separately to the PC used to get the data from the device as a long cable is supplied and extension cabling is available. • Figure 2.1: Boltek StormTracker™ Boltek® LD-250 • Figure 2.2: Boltek LD-250 This device is similar to the StormTracker™ system but instead of a PCI card has a separate box which connects to the PC via a USB or RS232 (serial) port. 5 Chosen System It was decided that the StormTracker™ system should be employed for this project as it can fit tidily inside a computer and the card itself does not process the data, this is left to the software (the LD-250 box analyses the data to an extent and may remove some of the information required about the strikes). Installation To ensure accurate detection of strikes and determination of distances and directions of strikes, the antenna unit is to be installed away from all metal objects (as these can attenuate the signal making strikes appear further away than they actually are) and sources of radio noise such as computer equipment and electrical cables (these can cause non-existent strikes to be detected). To get the best range from the detector, the antenna unit should be installed as high as possible with a direct line of site to the horizon. To achieve the best results, the lightning detector system was installed on the roof of building R25 with the antenna enclosed in a plastic pipe to protect it, attached to the side of a fibreglass hut which was to house the computer system to obtain the data from the detector. The lightning detector system was installed by Ionosondes group engineer John Smith and RAL Ground Station engineer John Wright. • Figure 2.3: Boltek® StomTracker™ (antenna in grey pipe) installed on the roof of building R25. 6 Software With the Boltek StormTracker™ lightning detector, there are a number of software options available to obtain the lightning strike data and put it into a form suitable for this research project. Boltek StormTracker™ This is the simple package that is supplied with the StormTracker™ lightning detector that allows only for simple plotting of strikes on a map and for tracking thunderstorms. This will not be suitable for the project as detailed quantitative data about each strike (including the type and polarity) is not available. • Figure 2.4: Boltek StormTracker™ Software Lightning/2000 • Figure 2.5: Lightning/2000 Software Lightning/2000 from Aninoquisi (http://www.aninoquisi.com/lightning2000.htm) offers considerably more than the StormTracker™ software in terms of the data that is available from it. It works with the Boltek StormTracker™ to provide detailed information about lightning strikes including the type (cloud-to-ground or intra-cloud) and polarity of the strike as well as storm tracking facilities and the ability to export 7 screenshots and storm tracking reports, however the raw lightning strike data cannot be exported from the program. NexStorm • Figure 2.5: NexStorm Software NexStorm from Astrogenic systems (http://www.astrogenic.com/nexstorm) can provide a similar level of detail about individual lightning strikes picked up by a Boltek StormTracker™ or LD-250 detector but as well as exporting screenshots and storm tracking reports (which it generates using its TRAC system – Thunderstorm Ranging and Acquisition). It also puts the strike data into a shared memory area on the system so that it can be read by other programs. It puts all data into the memory but marks those it classes as radio noise not caused by a strike by setting the distance and bearing of the strike to -1. Because it makes raw lightning strike data available to other programs via a shared memory area, NexStorm was selected to get the data from the detector and a small application will be written to obtain the data and put it in the World Data Centre database. 8 Software Design 3 Chapter Task of the Software In order to utilise the data from NexStorm™ and the Boltek® StormTracker™, an application was written to take the data from NexStorm’s shared memory space and output it to the World Data Centre database (using the PostgreSQL SQL database system) for use in the research project. The software must: • Read the data from memory and output it to the PostgreSQL server without causing memory access violations or having a too greater toll on network bandwidth or system resources. • Run stably and continuously without any user interaction. • Be configurable by the end user (output settings etc.). • Be easy to configure and use. The software was written in the language C++ as this provides a lot of advanced functions such as memory access and database access (via function libraries for each database system) as well as native support for Microsoft® Windows® based GUI (graphical user interface) construction with little complex coding for such trivial tasks using the Microsoft® Foundation Class library (MFC). Main Routines Before work began on the main application to obtain data, research was done into the options for accessing the memory and the PostgreSQL database. Memory Access NexStorm uses a system known as FlashGate IPC (inter-process communication) to share the lightning strike data using the system memory where the memory location is accessed by a name so can be in any physical location and still accessed. Astrogenic Systems provide an example application to access the shared memory location with C++ source code with NexStorm, which was studied to learn how to access shared memory under Microsoft® Windows® using the FlashGate IPC system. 9 • Figure 3.1: NexStorm example FlashGate IPC client program To access the shared memory area, initially the file mapping object for the memory area (already created with a specific name by NexStorm) is opened, then the ‘file’ (the contents of the memory) is mapped into a variable in the address space of the client program so that the data can be read. The data can then be accessed like any other variable in that application. To ensure memory access violations do not occur (which can cause the software to ‘crash’), semaphores can be employed in the application. These are used to control access to memory locations and to ensure that only one process attempts to access the data at once. A semaphore is created for the memory location and then the thread waits for the semaphore to change state signalling that the memory location is ready for access. When the program has finished reading the memory, the semaphore is released so that another process can gain access. All of the data is stored in memory as a single string, which is divided up and put into the appropriate variables, one for each parameter, by a C function. A simple console-based application was made to test this code, which reads the shared memory space and outputs the contents to the console window. This code was then known to be suitable to be made a module in the final application. • Figure 3.2: Test console-based application to obtain data from shared memory area. PostgreSQL Database Access PostgreSQL provide the well documented libpq function library which contains C functions for accessing a UNIX-based PostgreSQL database. It is possible to use 10 these function in Windows® based software if the supplied libpq.dll file is compiled and registered on the machine that will run the software. To access the database, first a connection is opened to the database on the SQL server (authenticated with a username and password), and then the SQL command (INSERT to add data to the database) is passed as a string to a function that executes the command on the SQL server. The connection to the SQL server is then closed. The result of executing the function can be obtained to either acquire data or check it worked. A console-based application was written to test this code by inputting arbitrary data into the database. This code was also suitable to be made into a module for the final application. Initial Design for the Software The program will take an object-oriented design with an ‘object’ for the lightning strike data that can itself obtain the data and output it to the SQL server as well as to a text file (this was added as a backup for when a network connection to the SQL server is not available). The user initially configures the software (how frequently the memory is read, the text file to put data into and the SQL server connection settings) in a dialog and then opts to begin data acquisition. From this point, as frequently as set by the user, the software calls a method of the strike data object that reads the memory and gets the lightning strike data. It then checks that it is not radio noise not caused by a strike (determined by NexStorm) or a ‘heartbeat’ from NexStorm (a signal to show NexStorm is still working) and if it is a new strike (NexStorm keeps the previous piece of data in memory until more is received). The data is then written, by other methods, to the text output file and to the SQL server (unless the user opts not to output data to either of these). This continues until the user sends the command from the dialog to stop data acquisition. Problems Encountered with the Software In the initial build, a number of issues were encountered with the software. Time Delay Too Long Between Memory Reads Initially, the software would write to the SQL buffer as soon as it found a strike in memory. This process can take a considerable amount of time (up to a quarter of a second for SQL writes) which significantly reduces the resolution of the system (causing it to miss some strikes). To overcome this, a technique known as multithreading was employed, where instead of executing commands one by one in order, the system sends multiple commands (or ‘threads’) through the processor simultaneously. Instead of waiting for one data acquisition run to complete before starting a timer to begin the next, every so often (time set by user), a new thread is started to read the memory and to place the data in the appropriate location(s); a new run can begin before the last had ended. This approach introduced a number of problems… 11 Data Not Being Placed in SQL Table Using the multithreading technique, too many commands were being sent too quickly to the SQL server and it was rejecting some of them, therefore not all of the data was being recorded in the database. This was overcome by buffering SQL commands. Where the original build would concatenate a command then send it to an SQL server, the command would just be produced then stored in an STL vector (an array of variables stored together as one unit) of strings (constructs that manage their own memory usage to control use of system resources). Then, after so many memory reads, when this buffer became full or when the STOP button is clicked, all of these commands would be sent, as a single transaction, to the SQL server. Buffering the commands eliminates the need to start each data acquisition run in a separate thread (as it is only really the writes to the SQL server that slow the process down). Removing these threads will increase the reliability of the software as fewer threads attempting to access the memory will decrease the likelihood of access violations. The commands will be copied to another, working buffer and sent to the SQL server in a separate thread so data acquisition can continue while the write takes place. High Memory Usage When monitored with the Windows® Task Manager, the memory usage of the application appeared to increase rapidly until it stopped at around 130 MB, which is sufficiently high to imply a problem exists within the code. It also appeared that after a while, data acquisition would cease, which would suggest that Windows® is not allowing the software to use any more memory so it cannot continue to operate. Various parts of the code were studied to find the cause of this problem and it was found to be caused by opening the mapping object for the shared memory. The file mapping object would be created each time the memory was read but not destroyed at the end so several mapping objects would exist filling the memory. To overcome this issue, the code was altered so to only map the view of the memory space when the START button is clicked rather than each time the memory needs to be read. Final Build After these alterations were made, the user interface of the software was designed to be as clear and intuitive as possible with the following features: • Single dialog box to set up all data acquisition options from one screen using clear controls before sending the command to begin data acquisition. • Tolerance to user error – all options set by user are checked to ensure useful results will be produced and no system errors will occur (e.g. a valid filename must be entered and the options for connecting to the SQL server are checked when data acquisition begins so no time is wasted or data is lost). • Because the software is designed to run in the background, the windows was set to minimise to an icon in the Windows® system tray which changes depending on what the software is doing. This keeps the tool out of the way so the user can use other parts of the system (for example NexStorm itself). 12 • Figure 3.3: The main dialog for the Lightning Data Acquisition Tool running on Microsoft® Windows® XP. An option to log events with a date and time was added to the program to assist with the debugging procedure to show when the user changes any major settings and when failures occur. The flow chart in Appendix ii outlines the workings of the software. The code of the data acquisition module can be found in Appendix iii. Testing Before the final version of this software was compiled and commissioned for use with the lightning detector it was extensively tested using a range of options including those that are known to cause errors (such as invalid filenames or SQL server host/port names that do not exist) and the response of the program checked. A similar project to detect lightning strikes is being run in Japan (http://hp.Vector.co.jp/authors/VA034934/Lightning) and those running it have made a piece of software available to read NexStorm’s shared memory area and write the data to a comma-separated text file and to the screen (NSList) like the tool that has been made. The data collected from this software was compared with the data collected by the tool in development to check how accurate the data was and how good the resolution was (the number of strikes it received). Most of the time, every strike picked up by NSList was written to the database by the tool in development if the time interval between reads is set to a sufficiently low value. • Figure 3.4: NSList for Japanese project 13 Using the Data 4 Chapter Using the Data for Research The purpose of the research project is to compare Ionospheric data with that about lightning strikes in the vicinity of the Chilton ionosonde. To obtain the most conclusive results, the quantitative data containing the time, position, type and polarity of the strike will be required; therefore the raw data must be in a usable form in the PostgreSQL database that the Lightning Data Acquisition Tool placed it into. Table 4.1 shows the data stored about each individual lightning strike detected in the SQL database. Field count Data Type integer year month day time integer integer integer text timestamp bearing distance integer integer integer x y strike_type integer integer text polarity text tracbearing integer tracdistance integer tracx tracy integer integer Description A unique identification given to each strike by NexStorm, used to confirm each strike is new data. The data of the lightning strike. The time of the strike in the form H:mm:ss, calculated by the Lightning Data Acquisition Tool. The time of the strike in seconds from 0:00 midnight. The bearing of the strike in 10th of degrees from North. The distance of the strike in kilometres from the detector. Co-ordinates of the strike on a 1000x1000 pixel map with the detector in the centre. Calculated by NexStorm. The type of the strike: C = intra-cloud, G = cloud to ground. The polarity of the cloud emitting the strike (comparable to the direction of electron flow in the strike): + = positive, - = negative. The bearing of the thunderstorm (determined by NexStorm’s TRAC – Thunderstorm Ranging and Acquisition engine) in 10th of degrees from north. The distance, in kilometres, of the thunderstorm from the detector. Co-ordinates of the thunderstorm on a 1000x1000 pixel map with the detector in the centre. • Table 4.1: Lightning Strike Data database schema Figure 4.1 shows the data being accessed directly from the database using the pSQL PostgreSQL client for UNIX. A number of flexible queries can be passed to the database server to get the data required. 14 • Figure 4.1: Lightning strike data being accessed via the UNIX pSQL client. Graphical Display of Data As well as the raw data in the database, it would be useful to have graphical means of displaying the lightning strike data. It was decided that IDL® (the Interactive Data Language) should be used to plot the data as this provides a powerful set of tools to build many custom charts and can execute a precompiled set of commands to automate the plotting procedure. Data can be imported from a number of sources. IDL® also provides a number of tools to draw maps of the world (built-in) and superimpose plots of data on top of the map. The data, including plots will be made available on the World Data Centre web site and will update automatically, therefore Perl scripts were written to compile the IDL® command file containing the data from the database to plot the data as a GIF file which is in the web page. Perl was chosen as it allows powerful scripts to be written quickly and it has a number of modules to add features such as PostgreSQL database access. Mapping Lightning Strikes It would be useful to have a plot showing the location of each lightning strike to quickly see the situation. This can be achieved by calculating the longitude and latitude of each strike from the distance and bearing of the strike and the location of the detector, then plotting these points straight onto the map of the appropriate area. It was decided that each plot should show the strikes that occurred within the last two hours, using a colour code to separate the times of the strikes into twenty minute intervals and different symbols to distinguish the type and polarity of each strike. The date and time period of the data shown is automatically placed at the top of each plot. 15 • Figure 4.2: Map of lightning strikes within a two hour period. The symbols correspond to the type and polarity of the strike and the colour to the time. Plotting the Strike Rate IDL® also allows contour plots to be produced where each line corresponds to, in this case, the lightning strike rate (which will be calculated by the Perl script compiling the commands). Each time a plot is produced, IDL® will automatically determine the scaling of the contour lines and colouring will be used to distinguish them (yellow showing the most frequent strikes). These will be plotted over the map. • Figure 4.3: Contour plot of the lightning strike rate superimposed onto the map. 16 Calibration 5 Chapter Calibrating the System A number of factors can introduce inaccuracies into the data collected by the lightning detector. The main source of inaccuracy lies in the distance readings where objects (particularly metal) attenuate the signal causing the lightning strike to appear further away than it actually is (as the distance is determined from the amplitude of the signal). The system must take account of this for the data collected to be reliable. NexStorm allows the strike ranging to be corrected either by applying a global adjustment to the data or by only adjusting data of strikes in particular regions. To accurately calibrate the system, data must be collected over a long period of time and compared with other sources of lightning strike data which is available from a number of locations: The Met. Office The United Kingdom Met. Office sells lightning strike data they have collected from their detectors and are considered a reliable source of accurate information. Other Lightning Detection Systems A number of other lightning detection systems have been set up and some including Island Stock (http://www.islandstock.com/weather), based in Jersey make the maps available on the internet which can be compared with the ones produced by this detection system. However it is unknown how reliable such data is. • Figure 5.1: Lightning Strike Data from the Island Stock website, based in Jersey 17 Nimrod Rain Radar Data Thunderstorms are generally accompanied by heavy rain, therefore the locations of the lightning strikes detected can be compared with the locations of heavy rainfall detected by the Nimrod rain radar system to which the British Atmospheric Data Centre at RAL have access. • Figure 5.2: Rainfall data from the Nimrod rain radar system 18 6 Chapter Conclusions Achievements and Future of the Project During this project, a lightning strike data collection system was designed and incorporated into the existing computing infrastructure in the World Data and it is in such a state that data can be collected and used. Before the data collected can be considered sufficiently reliable to be used for research, the system must be properly calibrated to ensure reliability of the data. This will require a lot of data to be collected over time and compared with data collected from other systems. Once the system has been properly calibrated, the research can begin into how electrical activity in the form of lightning strikes affects the ionosphere using the data from the system as well as the graphical representations produced. The data is stored in the database in such a flexible way that the data can be analysed in ways other than those produced by the scripts already written and these other methods can be created when they are needed. 19 Appendices i. Flow of Data through System .............................................................21 ii. Software Data Acquisition System Flowchart....................................22 iii. C++ Code for Data Acquisition Tool ..................................................23 iv. Acknowledgements.............................................................................31 20 Appendix i: Flow of Data through System StromTracker Computer StormTracker Lightning Detector raw data NexStorm Lightning Data Collection Software strike data Shared System Memory read from memory formatted data output Lightning Data Acquisition Tool formatted data output C1 Cluster Comma separated text file PostgreSQL Database strike data strike data strike data pSQL Client to Access Data WDCC1 Web Server Strike Map Perl Script (CRON Job) IDL commands with data idl_sdplot001.pro IDL Command FIle Strike Rate Map Perl Script (CRON Job) execute execute IDL IDL plot plot GIF Image Plot GIF Image Plot image view image view HTML Page Showing Plot HTML Page Showing Plot 21 IDL commands with data idl_srplot001.pro IDL Command FIle Appendix ii: Software Data Acquisition System Flowchart User Starts Data Acquisition Read options from dialog box and store in structure. Disable options. Attempt to connect yes to server then close connection Connected to server OK? yes Data acquisition ends Output data to SQL? no Map view of NexStorm’s shared memory space for read access Re-enable options in dialog box no no Abort data acquisition and inform user Output data to text file? yes Close text output file Open text output file get result yes Is text output file open? no Create semaphore and wait for it to give access to shared memory yes Close the connection to the SQL server File opened OK? no no Send all the commands in a single transaction to the SQL server Abort data acquisition and inform user Is the semaphore open? End of data write thread yes Read string containing data from memory Copy SQL command buffer to temporary buffer and clear SQL command buffer Was the connection successful? no try max. 10 times Is the data noise, a heartbeat or the same as the previous? yes Open connection to SQL server and get result of action yes Print the data, separated by commas, into the text file Output data to SQL and commands remain in buffer? no yes Copy SQL command buffer to temporary buffer and clear SQL command buffer no yes Open connection to SQL server and get result of action yes no Add the SQL command to add the data to the table to the SQL command buffer no try max. 10 times Send all the commands in a single transaction to the SQL server Interpret time, type and polarity and put into appropriate formats Output data to text file? Was the connection successful? Close the connection to the SQL server Separate data contained in string and store in separate variables yes yes Output data to SQL? Has the user clicked the STOP button? no no no no Copy current data object to previous data object for comparison yes 22 Output data to SQL? yes Has the loop been round 6000 since last write or is the SQL command buffer full c:\Documents and Settings\d.r.wilkins\My ...\Visual Studio Projects\Ldat\acquireData.cpp // // // // // // // // // // /* 1 acquireData.cpp =============== Code to obtain data from NexStorm via FlashGate IPC Part of LDAT - by Daniel Wilkins Last Edited 19/08/2004 14:35 by D Wilkins Working release compiled 19/08/2004 14:00 by D Wilkins (C) Copyright 2004 D Wilkins and Ionosondes Group - CCLRC Rutherford Appleton Laboratory PostgreSQL access functions by PostgreSQL group - http://www.postgresql.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "stdafx.h" ////////////////////// IPC CONFIGURATION TO GET NEXSTORM DATA /////////////////////////// // change buffer settings below static const long bufferSize = 100; static const long buffers = 1; // change name of shared memory area below static const char memoryName[] = "NXFGIPC_SHMEM_0822931589443_238731_GATE0"; static char (*memory)[buffers][bufferSize]; static const char ReaderSemaphoreName[] = "Reader Semaphore"; static HANDLE hReaderSemaphore; static HANDLE hMemory; ///////////////////////////////////////////////////////////////////////////////////////// // **** FUNCTION DECLARATIONS **** void logToFile(char[1024]); void writeSql(); UINT thread_GetData(LPVOID); void startthread_GetData(int*, /*CONFIGURATION:*/unsigned int, bool, char, bool, char, char , char, char, bool, char, char, bool); // **** GLOBAL VARIABLE DECLARATIONS **** // stores pointer to exit signal int* exitSignal; // temporary string for concatenating log file entries char logString[1024]; // for text file output FILE* outFile; bool fileIsOpen = false; // signal to ensure file is open before attempting to write to it // for SQL buffering subsystem vector <string> sqlBuffer(6050); // use vectors of strings so system can manage memory vector <string> tmpSqlBuffer(6050); int sqlBufferCount = 0; int tmpSqlBufferCount = 0; ///////////////////////// STRUCTURE TO HOLD SETUP DATA ////////////////////////////////// struct Config { unsigned int interval; bool txtOut, sqlOut, sqlLogon, log; char txtFile[512], sqlTable[50], logFile[512]; string sqlConnSet; } setup; // define structure variable to hold configuration data ///////////////////////// CLASS FOR STRIKE DATA OBJECTS////////////////////////////////// class StrikeData /**********************************************************************/ /* Class for lightning strike data objects. */ c:\Documents and Settings\d.r.wilkins\My ...\Visual Studio Projects\Ldat\acquireData.cpp /* Holds all the data in private data items except count and year. */ /* HOLD DATA ITEMS */ /* - count (unique id given to each strike by NexStorm) */ /* - year, month, day of the strike */ /* - timestamp of the srtike (in seconds) */ /* - Distance, bearing, x-co-ordinate and y-co-ordinate of strike */ /* both raw values and those corrected by NexStorm's TRAC system */ /* - strike type (intercloud or cloud-ground) */ /* - strike polarity */ /* PUBLIC FUNCTIONS TO: */ /* - check if strike is noise - bool isNoise() */ /* - check if strike is a heartbeat from NexStorm - bool isHeartbeat()*/ /* - check data is valid - bool isValidStrike() */ /* - output data to a text file - void toFile(bool init) */ /* - output data to SQL buffer to be written later */ /**********************************************************************/ { public: // public so other functions can see if it is a new strike unsigned int count; private: unsigned int year, month, day, timestamp; char time[8]; // holds time in format H:mm:ss int tracBearing, tracDistance, rawBearing, rawDistance, tracX, tracY, rawX, rawY, rawStrikeType, rawStrikePolarity; char strikeType; char strikePolarity; public: void getData() /****************************************************************/ /* Obtains data from FlashGate IPC (NexStorm's shared memory). */ /* Stores data in the object. */ /* Takes no arguments and returns no value. */ /****************************************************************/ { string recvData; DWORD semaphoreStatus; // create read semaphore to control access to the memory - protects data structure hReaderSemaphore = CreateSemaphore(NULL, 0, buffers, ReaderSemaphoreName); //-------------ERROR HANDLING-----------// if(hReaderSemaphore == NULL) { logToFile("Error creating read semaphore. Could not get access to NexStorm 's IPC memory."); return; } // wait time set as interval for state signal from semaphore object semaphoreStatus = WaitForSingleObject(hReaderSemaphore, setup.interval); // proceed only if semaphore allows it if(semaphoreStatus == 0) { // read in data from memory recvData = (*memory)[0]; // put received data from string into the separate variables 2 c:\Documents and Settings\d.r.wilkins\My ...\Visual Studio Projects\Ldat\acquireData.cpp sscanf(recvData.c_str(), "%u,%u,%u,%u,%u,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", &count, &year, &month, &day, ×tamp, &tracBearing, &tracDistance, &rawBearing, &rawDistance, &tracX, &tracY, &rawX, &rawY, &rawStrikeType, &rawStrikePolarity); // interpret strike type and polarity to display as symbol strikeType = (rawStrikeType == 0) ? 'G' : 'C'; strikePolarity = (rawStrikePolarity == 0) ? '+' : '-'; // put time into correct format getTime(); } ReleaseSemaphore(hReaderSemaphore, 1, NULL); } void getTime() /****************************************************************/ /* Gets the time in the format H:mm:ss from the raw timestamp */ /* in seconds and stores it into the object's time structure. */ /* Takes no arguments and returns no value. */ /****************************************************************/ { int hr, min, sec; min = timestamp / 60; // divide by 60 to find minutes sec = timestamp % 60; // seconds is equal to the remainder hr = min / 60; min = min % 60; char outMin[5], outSec[5]; // ensure values are double-digit for minutes and seconds if(min < 10) sprintf(outMin,"0%d",min); else sprintf(outMin,"%d",min); if(sec < 10) sprintf(outSec,"0%d",sec); else sprintf(outSec,"%d",sec); sprintf(time,"%d:%s:%s", hr, outMin, outSec); } bool isNoise() /****************************************************************/ /* Checks if the received strike data is 'noise'. */ /* If signal is noise, NexStorm sets distance and bearing to -1 */ /* Takes no arguments */ /* Returns BOOLEAN value true if it is noise, false if not. */ /****************************************************************/ { if(rawDistance == -1 && rawBearing == -1) return true; else return false; } bool isHeartbeat() /************************************************************************/ /* Checks if the received strike data is a 'heartbeat' */ /* If signal is a 'heartbeat', NexStorm sets distance and bearing to -9 */ /* Takes no arguments */ /* Returns BOOLEAN value true if it is a 'heartbeat', false if not. */ /************************************************************************/ 3 c:\Documents and Settings\d.r.wilkins\My ...\Visual Studio Projects\Ldat\acquireData.cpp 4 { if(rawDistance == -9 && rawBearing == -9) return true; else return false; } bool isValidStrike() /************************************************************************/ /* Checks if the received strike data is a valid */ /* Checks it is not noise or a heartbeat and ensures actual lightning */ /* data is there (and not arbitrary values in the memory). */ /* Returns BOOLEAN value true if it is valid data, false if not. */ /************************************************************************/ { if(!isNoise() && !isHeartbeat() && (year > 1950) && (year < 4000)) return true; else return false; } void toFile() /****************************************************************/ /* Writes the properties of the StrikeData object to text file */ /* Requires FILE* outFile to already be opened for the file to */ /* write to. */ /* Takes one BOOLEAN argument - initialisation of file */ /* - TRUE will print headers to file */ /* - FALSE will print the data values */ /* Returns no value. */ /****************************************************************/ { char output[1024]; sprintf(output,"%u , %u , %u , %u , %s , %u , %d , %d , %d , %d , %c , %c , %d , %d \n", count, year, month, day, time, timestamp, rawBearing, rawDistance, rawX, rawY, strikeType, strikePolarity, tracBearing, tracDistance, tracX, tracY); // print to ouput file (already opened) fprintf(outFile,"%s",output); } void toSQL() /****************************************************************/ /* Writes the data in the StrikeData object to SQL buffer */ /* to be written later to the SQL database by writeSql(). */ /* Takes no arguments and returns no value. */ /****************************************************************/ { char sqlCommand[512]; sprintf(sqlCommand,"INSERT INTO %s VALUES (%u,%u,%u,%u,\'%s\',%u,%d,%d,%d,%d,\'%c\' ,\'%c\',%d,%d,%d,%d)", setup.sqlTable, /*STRIKE DATA:*/count, year, month, day, time, timestamp, rawBearing, rawDistance, rawX, rawY, strikeType, strikePolarity, tracBearing , tracDistance, tracX, tracY); sqlBuffer[sqlBufferCount] = sqlCommand; sqlBufferCount++; } }; //////////////////////////////////////////////////////////////////////////////////////// void logToFile(char text[1024]) /****************************************************************/ /* Passed a string which is logged to the text file (specified */ /* in the setup structure if set up to log) along with the */ /* current date and time. */ /* Returns no value. */ /****************************************************************/ { if(setup.log == true) { FILE* logFile = fopen(setup.logFile,"a"); time_t rawtime; c:\Documents and Settings\d.r.wilkins\My ...\Visual Studio Projects\Ldat\acquireData.cpp struct tm * timeinfo; // get time from system time(&rawtime); timeinfo = localtime(&rawtime); fprintf(logFile, "%s: %s\n", asctime(timeinfo), text); fclose(logFile); } } UINT writeSql(LPVOID pParam) /****************************************************************/ /* Connects to SQL database using settings in the setup struct. */ /* Then writes the contents of the temporary (working) SQL */ /* command buffer (which was copied from the buffer) to the SQL */ /* database. /* Takes one argument argument - NULL - needed for threading. */ /* Returns 0 after successful write, 1 if connection failed. */ /****************************************************************/ { PGconn* sqlConn; // connect to PostgreSQL database for(int connectCount = 0; connectCount < 10; connectCount++) { sqlConn = PQconnectdb(setup.sqlConnSet.c_str()); //--------------------- ERROR HANDLING -----------------------// if(PQstatus(sqlConn) != CONNECTION_OK) { logToFile("Failed to connect to SQL server. Will try again."); if(connectCount == 10) { logToFile("Giving up connecting to SQL."); return 1; } } else break; } PQexec(sqlConn, "BEGIN"); for(int bufferIndex = 0; bufferIndex <= tmpSqlBufferCount; bufferIndex++) { PQexec(sqlConn, tmpSqlBuffer[bufferIndex].c_str()); //CHANGED FROM TEMP BUFFER } PQexec(sqlConn, "COMMIT"); PQfinish(sqlConn); tmpSqlBufferCount = 0; return 0; } UINT thread_GetData(LPVOID pParam) /****************************************************************/ /* Function for data acquisition thread */ /* Opens text file if necessary then calls the member function */ /* to get the strike data from NexStorm's shared memory every. */ /* so often. Then closes file. */ /* Returns 0 if it completed successfully, 1 if it fails. */ /* Argument does nothing -required by MFC start thread function.*/ /****************************************************************/ { // open the file mapping object for the shared memory space hMemory = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, sizeof(char [buffers][bufferSize]), memoryName); // map view of the file into LDAT's address space so it can be accessed memory = (char (*)[buffers][bufferSize])MapViewOfFile(hMemory, FILE_MAP_WRITE, 0, 0, sizeof(char [buffers][bufferSize])); StrikeData currentStrike; // must retain its value when function returns to check next strike static StrikeData previousStrike; 5 c:\Documents and Settings\d.r.wilkins\My ...\Visual Studio Projects\Ldat\acquireData.cpp 6 // open text file if necessary if(setup.txtOut == true) { // open output file for append sprintf(logString,"Opening %s for data output.",setup.txtFile); logToFile(logString); outFile = fopen(setup.txtFile, "a"); fileIsOpen = true; //--------------------- ERROR HANDLING -----------------------// if(outFile == NULL) { MessageBox(NULL,"The specified output file could not be opened. Please ensure a valid filename was entered (if the file does not exist it will be created) and that it is not in use by another process or user.\n\nData acquisition will not continue. Press STOP to reset.", "Lightning Data Acquisition Tool", MB_ICONERROR); logToFile("Failed to open file for output."); fileIsOpen = false; logToFile("Data acquisition ABORTED due to error.\n============"); return 1; } fprintf(outFile,"Count , Year , Month , Day , Time , Timestamp , Bearing , Distance , X , Y , Strike Type , Strike Polarity , TRAC Bearing , TRAC Distance, TRAC X , TRAC Y\n==================================================================================== ======================================================================\n"); } logToFile("Starting data acquisition."); for(int acquireCount = 0; *exitSignal == 0; acquireCount++) { // call function to get strike data from shared memory currentStrike.getData(); if(currentStrike.isValidStrike() && (currentStrike.count != previousStrike.count)) //check it is valid data and not a duplicate { previousStrike = currentStrike; if(fileIsOpen == true) // ensure file is actually open before writing currentStrike.toFile(); if(setup.sqlOut == true) currentStrike.toSQL(); } // write contents of buffer to SQL if necessary if(setup.sqlOut == true && (/*Time in 10ms:*/acquireCount >= 6000 || sqlBufferCount >= 6000) && sqlBufferCount > 0) { // copy SQL buffer to temp working buffer tmpSqlBuffer = sqlBuffer; tmpSqlBufferCount = sqlBufferCount; acquireCount = 0; sqlBufferCount = 0; AfxBeginThread(writeSql, NULL); } // wait before running it again Sleep(setup.interval); } // close text file if necessary if(fileIsOpen == true) { fclose(outFile); fileIsOpen = false; logToFile("Closed output file."); } // write any remaining data to databse and disconnect if necessary if(setup.sqlOut == true && sqlBufferCount > 0) { tmpSqlBuffer = sqlBuffer; tmpSqlBufferCount = sqlBufferCount; acquireCount = 0; sqlBufferCount = 0; writeSql(NULL); } logToFile("Data acquisition stopped by user.\n============"); return 0; c:\Documents and Settings\d.r.wilkins\My ...\Visual Studio Projects\Ldat\acquireData.cpp 7 } void startthread_GetData(int* exitPtr, /*CONFIGURATION:*/unsigned int interval, bool txtOut , char txtFile[512], bool sqlOut, char sqlServer[50], char sqlPort[5], char sqlDBName [50], char sqlTable[50], bool sqlLogon, char sqlUsername[50], char sqlPassword[50], bool log, char logFile[512]) /****************************************************************/ /* Function to start data acquisition thread */ /* Uses UINT thread_GetData(LPVOID pParam) function. */ /* Puts the configuration into the config structure */ /* Passed 13 arguments */ /* - int* for pointer to exit signal variable */ /* - 12 variables for program configuration */ /* Returns no value. */ /****************************************************************/ { exitSignal = exitPtr; //receives pointer to the exit signal and makes it accessible to functions in this file // get configuration from arguments and store in structure setup.interval = interval; setup.txtOut = txtOut; sprintf(setup.txtFile,"%s",txtFile); setup.sqlOut = sqlOut; sprintf(setup.sqlTable,"%s",sqlTable); setup.log = log; sprintf(setup.logFile, logFile); // check log file if(setup.log == true) { FILE* testLogFile = fopen(setup.logFile,"a"); if(testLogFile == NULL) { MessageBox(NULL,"The specified log file could not be opened. Please ensure a valid filename was entered (if the file does not exist it will be created) and that it is not in use by another process or user.\n\nData acquisition will not continue. Press STOP to reset.", "Lightning Data Acquisition Tool", MB_ICONERROR); return; } fclose(testLogFile); } sprintf(logString,"Data acquisition started by user - Interval = %dms",setup.interval); logToFile(logString); // check connection to SQL server if necessary if(setup.sqlOut == true) { if(sqlLogon == true) { setup.sqlConnSet = string("host=") + sqlServer + " port=" + sqlPort + " dbname= " + sqlDBName + " user=" + sqlUsername + " password=" + sqlPassword + " connect_timeout =10"; sprintf(logString,"Attempting to connect to SQL database %s on port %s of server %s as user %s.", sqlDBName, sqlPort, sqlServer, sqlUsername); } else { setup.sqlConnSet = string("host=") + sqlServer + " port=" + sqlPort + " dbname= " + sqlDBName + " connect_timeout=10"; sprintf(logString,"Attempting to connect to SQL database %s on port %s of server %s.", sqlDBName, sqlPort, sqlServer); } logToFile(logString); PGconn* sqlConn; sqlConn = PQconnectdb(setup.sqlConnSet.c_str()); //--------------------- ERROR HANDLING -----------------------// if(PQstatus(sqlConn) != CONNECTION_OK) { MessageBox(NULL,"An error occurred while attempting to connecting to the PostgreSQL server. Please verify the connection settings and ensure you have access to the specified database.\n\nData acquisition will not continue. Press STOP to reset.", "Lightning Data Acquisition Tool", MB_ICONERROR); c:\Documents and Settings\d.r.wilkins\My ...\Visual Studio Projects\Ldat\acquireData.cpp logToFile("Failed to connect to SQL server."); logToFile("Data acquisition ABORTED due to error.\n============"); return; } logToFile("Connected successfully to SQL server."); PQfinish(sqlConn); } // start the timer function in a new thread (send no arguments) AfxBeginThread(thread_GetData, NULL); } 8 iv Acknowledgements Appendix Thanks must go to all those who have helped with this project: • Chris Davis for providing and running the project for the Nuffield Science Bursary Scheme and the Nuffield Science Bursary scheme organisers including regional co-ordinator Bridget Holligan. • The ionosondes group at CCLRC Rutherford Appleton Laboratory including Sarah James, Richard Henwood and Richard Stamper for their help. • Matthew Wild for computing support. • Ionosondes group engineer John Smith and RAL Ground Station Engineer John Wright for installing the lightning detector. • David Hooper in the RAL British Atmospheric Data Centre (BADC) for Nimrod Rain Radar data. Parts/Systems Used in this Project • StormTracker™ lightning detector from Boltek®: http://www.boltek.com • NexStorm™ lightning data acquisition software for use with Boltek® lightning detectors and example FlashGate IPC client code made by Astrogenic Systems: http://www.astrogenic.com • NSList NexStorm FlashGate IPC client from Lite-Detect project page: http://hp.Vector.co.jp/authors/VA034934/Lightning/ • C++ source code written and compiled using Microsoft Visual C++ as part of Microsoft® Visual Studio® .NET 2003 Professional: http://msdn.microsoft.com/vstudio. • Plots produced using IDL® from RSI®, Research Systems Inc.: http://www.rsinc.com • PostgreSQL database system and pSQL UNIX command line client by the PostgreSQL group: http://www.postgresql.org Acknowledgements for Content of this Report • Information on Ionosphere (Chapter 1, page 3) adapted from “Ionosphere”, Microsoft Encarta 2000 and information about ionospheric layers and monitoring systems from the RAL Ionosondes group web page: http://www.wdc.rl.ac.uk/ionosondes as well as discussion with Chris Davis. • Figures 2.1 and 2.2, page 5 and figure 2.4, page 7 from the Boltek website: http://www.boltek.com • Figure 2.5, page 7 from the Aninoquisi Lightning/2000 page: http://www.aninoquisi.com/lightning2000.htm • Screenshot (Figure 5.1) on page 17 taken from Island Stock, Jersey: http://www.islandstock.com/weather 31