More time_t -> milliseconds conversions.

This lets us support fractional net flush times among other things, which is handy.
This commit is contained in:
Oliver Jowett 2015-02-10 22:24:22 +00:00
parent 7053ad02da
commit f9ed7e4a97
9 changed files with 62 additions and 111 deletions

View file

@ -136,6 +136,14 @@ is_number() {
if echo "$1" | grep -Eq '^([+-]?[0-9][0-9]*)(\.[0-9]+)?$'; then return 0; else return 1; fi if echo "$1" | grep -Eq '^([+-]?[0-9][0-9]*)(\.[0-9]+)?$'; then return 0; else return 1; fi
} }
is_unsigned_number() {
if echo "$1" | grep -Eq '^([+]?[0-9][0-9]*)(\.[0-9]+)?$'; then return 0; else return 1; fi
}
is_positive_number() {
if echo "$1" | grep -Eq '^(([+]?0\.[0-9]*[1-9]+[0-9]*)|([+]?[1-9][0-9]*)(\.[0-9]+)?)$'; then return 0; else return 1; fi
}
is_number_or_empty() { is_number_or_empty() {
if [ -z "$1" ]; then return 0 if [ -z "$1" ]; then return 0
elif is_number "$1"; then return 0; elif is_number "$1"; then return 0;
@ -198,15 +206,15 @@ db_go || true; db_get $NAME/auto-start; if [ "$RET" = "true" ]; then
db_input_verify low $NAME/net-bo-port is_port_number || true db_input_verify low $NAME/net-bo-port is_port_number || true
db_input_verify low $NAME/net-sbs-port is_port_number || true db_input_verify low $NAME/net-sbs-port is_port_number || true
db_input_verify low $NAME/net-fatsv-port is_port_number || true db_input_verify low $NAME/net-fatsv-port is_port_number || true
db_input_verify low $NAME/net-heartbeat is_unsigned_int || true db_input_verify low $NAME/net-heartbeat is_unsigned_number || true
db_input_verify low $NAME/net-out-size is_unsigned_int || true db_input_verify low $NAME/net-out-size is_unsigned_int || true
db_input_verify low $NAME/net-out-interval is_unsigned_int || true db_input_verify low $NAME/net-out-interval is_unsigned_number || true
db_input_verify low $NAME/net-buffer is_unsigned_int || true db_input_verify low $NAME/net-buffer is_unsigned_int || true
db_input_verify medium $NAME/net-bind-address is_ipaddrish_or_empty || true db_input_verify medium $NAME/net-bind-address is_ipaddrish_or_empty || true
db_input_verify low $NAME/stats-interval is_unsigned_int || true db_input_verify low $NAME/stats-interval is_unsigned_int || true
db_input_verify low $NAME/json-interval is_positive_int || true db_input_verify low $NAME/json-interval is_positive_number || true
db_input low $NAME/json-location-accuracy || true db_input low $NAME/json-location-accuracy || true
db_input low $NAME/json-dir || true db_input low $NAME/json-dir || true

View file

@ -317,10 +317,18 @@ Template: dump1090-mutability/invalid-is_ipaddrish_or_empty
Description: Value must be an IP address or empty. Description: Value must be an IP address or empty.
Type: error Type: error
Template: dump1090-mutability/invalid-is_number
Description: Value must be a decimal number
Type: error
Template: dump1090-mutability/invalid-is_number_or_empty Template: dump1090-mutability/invalid-is_number_or_empty
Description: Value must be a decimal number or empty. Description: Value must be a decimal number or empty.
Type: error Type: error
Template: dump1090-mutability/invalid-is_unsigned_number
Description: Value must be a non-negative number.
Type: error
Template: dump1090-mutability/invalid-is_valid_gain Template: dump1090-mutability/invalid-is_valid_gain
Description: Value must be a numeric gain value, or "max", or "agc". Description: Value must be a numeric gain value, or "max", or "agc".
Type: error Type: error

View file

@ -118,7 +118,7 @@ void modesInitConfig(void) {
Modes.net_fatsv_port = MODES_NET_OUTPUT_FA_TSV_PORT; Modes.net_fatsv_port = MODES_NET_OUTPUT_FA_TSV_PORT;
Modes.interactive_rows = getTermRows(); Modes.interactive_rows = getTermRows();
Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL; Modes.interactive_display_ttl = MODES_INTERACTIVE_DISPLAY_TTL;
Modes.json_interval = 1; Modes.json_interval = 1000;
Modes.json_location_accuracy = 1; Modes.json_location_accuracy = 1;
Modes.maxRange = 1852 * 300; // 300NM default max range Modes.maxRange = 1852 * 300; // 300NM default max range
} }
@ -588,41 +588,6 @@ MODES_DUMP1090_VARIANT " " MODES_DUMP1090_VERSION
); );
} }
#ifdef _WIN32
void showCopyright(void) {
uint64_t llTime = time(NULL) + 1;
printf(
"-----------------------------------------------------------------------------\n"
"| dump1090 ModeS Receiver Ver : " MODES_DUMP1090_VERSION " |\n"
"-----------------------------------------------------------------------------\n"
"\n"
" Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>\n"
" Copyright (C) 2014 by Malcolm Robb <support@attavionics.com>\n"
"\n"
" All rights reserved.\n"
"\n"
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
"\n"
" For further details refer to <https://github.com/MalcolmRobb/dump1090>\n"
"\n"
);
// delay for 1 second to give the user a chance to read the copyright
while (llTime >= time(NULL)) {}
}
#endif
static void display_total_stats(void) static void display_total_stats(void)
{ {
struct stats added; struct stats added;
@ -638,11 +603,11 @@ static void display_total_stats(void)
// from the net, refreshing the screen in interactive mode, and so forth // from the net, refreshing the screen in interactive mode, and so forth
// //
void backgroundTasks(void) { void backgroundTasks(void) {
static time_t next_stats_display; static uint64_t next_stats_display;
static time_t next_stats_update; static uint64_t next_stats_update;
static time_t next_json, next_history; static uint64_t next_json, next_history;
time_t now = time(NULL); uint64_t now = mstime();
icaoFilterExpire(); icaoFilterExpire();
trackPeriodicUpdate(); trackPeriodicUpdate();
@ -664,7 +629,7 @@ void backgroundTasks(void) {
int i; int i;
if (next_stats_update == 0) { if (next_stats_update == 0) {
next_stats_update = now + 60; next_stats_update = now + 60000;
} else { } else {
Modes.stats_latest_1min = (Modes.stats_latest_1min + 1) % 15; Modes.stats_latest_1min = (Modes.stats_latest_1min + 1) % 15;
Modes.stats_1min[Modes.stats_latest_1min] = Modes.stats_current; Modes.stats_1min[Modes.stats_latest_1min] = Modes.stats_current;
@ -686,11 +651,11 @@ void backgroundTasks(void) {
if (Modes.json_dir) if (Modes.json_dir)
writeJsonToFile("stats.json", generateStatsJson); writeJsonToFile("stats.json", generateStatsJson);
next_stats_update += 60; next_stats_update += 60000;
} }
} }
if (Modes.stats > 0 && now >= next_stats_display) { if (Modes.stats && now >= next_stats_display) {
if (next_stats_display == 0) { if (next_stats_display == 0) {
next_stats_display = now + Modes.stats; next_stats_display = now + Modes.stats;
} else { } else {
@ -835,13 +800,13 @@ int main(int argc, char **argv) {
Modes.net = 1; Modes.net = 1;
Modes.net_only = 1; Modes.net_only = 1;
} else if (!strcmp(argv[j],"--net-heartbeat") && more) { } else if (!strcmp(argv[j],"--net-heartbeat") && more) {
Modes.net_heartbeat_interval = atoi(argv[++j]); Modes.net_heartbeat_interval = (uint64_t)(1000 * atof(argv[++j]));
} else if (!strcmp(argv[j],"--net-ro-size") && more) { } else if (!strcmp(argv[j],"--net-ro-size") && more) {
Modes.net_output_flush_size = atoi(argv[++j]); Modes.net_output_flush_size = atoi(argv[++j]);
} else if (!strcmp(argv[j],"--net-ro-rate") && more) { } else if (!strcmp(argv[j],"--net-ro-rate") && more) {
Modes.net_output_flush_interval = atoi(argv[++j]) / 15; // backwards compatibility Modes.net_output_flush_interval = 1000 * atoi(argv[++j]) / 15; // backwards compatibility
} else if (!strcmp(argv[j],"--net-ro-interval") && more) { } else if (!strcmp(argv[j],"--net-ro-interval") && more) {
Modes.net_output_flush_interval = atoi(argv[++j]); Modes.net_output_flush_interval = (uint64_t)(1000 * atof(argv[++j]));
} else if (!strcmp(argv[j],"--net-ro-port") && more) { } else if (!strcmp(argv[j],"--net-ro-port") && more) {
if (Modes.beast) // Required for legacy backward compatibility if (Modes.beast) // Required for legacy backward compatibility
{Modes.net_output_beast_port = atoi(argv[++j]);;} {Modes.net_output_beast_port = atoi(argv[++j]);;}
@ -902,9 +867,10 @@ int main(int argc, char **argv) {
f++; f++;
} }
} else if (!strcmp(argv[j],"--stats")) { } else if (!strcmp(argv[j],"--stats")) {
Modes.stats = -1; if (!Modes.stats)
Modes.stats = (uint64_t)1 << 60; // "never"
} else if (!strcmp(argv[j],"--stats-every") && more) { } else if (!strcmp(argv[j],"--stats-every") && more) {
Modes.stats = atoi(argv[++j]); Modes.stats = (uint64_t) (1000 * atof(argv[++j]));
} else if (!strcmp(argv[j],"--snip") && more) { } else if (!strcmp(argv[j],"--snip") && more) {
snipMode(atoi(argv[++j])); snipMode(atoi(argv[++j]));
exit(0); exit(0);
@ -926,9 +892,9 @@ int main(int argc, char **argv) {
} else if (!strcmp(argv[j], "--write-json") && more) { } else if (!strcmp(argv[j], "--write-json") && more) {
Modes.json_dir = strdup(argv[++j]); Modes.json_dir = strdup(argv[++j]);
} else if (!strcmp(argv[j], "--write-json-every") && more) { } else if (!strcmp(argv[j], "--write-json-every") && more) {
Modes.json_interval = atoi(argv[++j]); Modes.json_interval = (uint64_t)(1000 * atof(argv[++j]));
if (Modes.json_interval < 1) if (Modes.json_interval < 100) // 0.1s
Modes.json_interval = 1; Modes.json_interval = 100;
} else if (!strcmp(argv[j], "--json-location-accuracy") && more) { } else if (!strcmp(argv[j], "--json-location-accuracy") && more) {
Modes.json_location_accuracy = atoi(argv[++j]); Modes.json_location_accuracy = atoi(argv[++j]);
#endif #endif
@ -983,7 +949,7 @@ int main(int argc, char **argv) {
if (Modes.net) modesInitNet(); if (Modes.net) modesInitNet();
// init stats: // init stats:
Modes.stats_current.start = Modes.stats_current.end = time(NULL); Modes.stats_current.start = Modes.stats_current.end = mstime();
// write initial json files so they're not missing // write initial json files so they're not missing
writeJsonToFile("receiver.json", generateReceiverJson); writeJsonToFile("receiver.json", generateReceiverJson);

View file

@ -180,7 +180,7 @@
#define MODES_INTERACTIVE_ROWS 22 // Rows on screen #define MODES_INTERACTIVE_ROWS 22 // Rows on screen
#define MODES_INTERACTIVE_DISPLAY_TTL 60000 // Delete from display after 60 seconds #define MODES_INTERACTIVE_DISPLAY_TTL 60000 // Delete from display after 60 seconds
#define MODES_NET_HEARTBEAT_INTERVAL 60 // seconds #define MODES_NET_HEARTBEAT_INTERVAL 60000 // milliseconds
#define MODES_NET_SERVICES_NUM 7 #define MODES_NET_SERVICES_NUM 7
#define MODES_NET_INPUT_RAW_PORT 30001 #define MODES_NET_INPUT_RAW_PORT 30001
@ -199,7 +199,7 @@
#endif #endif
#define HISTORY_SIZE 120 #define HISTORY_SIZE 120
#define HISTORY_INTERVAL 30 #define HISTORY_INTERVAL 30000
#define MODES_NOTUSED(V) ((void) V) #define MODES_NOTUSED(V) ((void) V)
@ -236,7 +236,7 @@ struct net_writer {
int connections; // number of active clients int connections; // number of active clients
void *data; // shared write buffer, sized MODES_OUT_BUF_SIZE void *data; // shared write buffer, sized MODES_OUT_BUF_SIZE
int dataUsed; // number of bytes of write buffer currently used int dataUsed; // number of bytes of write buffer currently used
time_t lastWrite; // time of last write to clients uint64_t lastWrite; // time of last write to clients
}; };
// Program global state // Program global state
@ -300,10 +300,10 @@ struct { // Internal state
int debug; // Debugging mode int debug; // Debugging mode
int net; // Enable networking int net; // Enable networking
int net_only; // Enable just networking int net_only; // Enable just networking
int net_heartbeat_interval; // TCP heartbeat interval (seconds) uint64_t net_heartbeat_interval; // TCP heartbeat interval (milliseconds)
int net_output_sbs_port; // SBS output TCP port int net_output_sbs_port; // SBS output TCP port
int net_output_flush_size; // Minimum Size of output data int net_output_flush_size; // Minimum Size of output data
int net_output_flush_interval; // Maximum interval (in seconds) between outputwrites uint64_t net_output_flush_interval; // Maximum interval (in milliseconds) between outputwrites
int net_output_raw_port; // Raw output TCP port int net_output_raw_port; // Raw output TCP port
int net_input_raw_port; // Raw input TCP port int net_input_raw_port; // Raw input TCP port
int net_output_beast_port; // Beast output TCP port int net_output_beast_port; // Beast output TCP port
@ -317,13 +317,13 @@ struct { // Internal state
int interactive; // Interactive mode int interactive; // Interactive mode
int interactive_rows; // Interactive mode: max number of rows int interactive_rows; // Interactive mode: max number of rows
uint64_t interactive_display_ttl;// Interactive mode: TTL display uint64_t interactive_display_ttl;// Interactive mode: TTL display
int stats; // Print stats at exit in --ifile mode uint64_t stats; // Interval (millis) between stats dumps,
int onlyaddr; // Print only ICAO addresses int onlyaddr; // Print only ICAO addresses
int metric; // Use metric units int metric; // Use metric units
int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...; int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...;
int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090 int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090
char *json_dir; // Path to json base directory, or NULL not to write json. char *json_dir; // Path to json base directory, or NULL not to write json.
int json_interval; // Interval between rewriting the json aircraft file uint64_t json_interval; // Interval between rewriting the json aircraft file, in milliseconds; also the advertised map refresh interval
int json_location_accuracy; // Accuracy of location metadata: 0=none, 1=approx, 2=exact int json_location_accuracy; // Accuracy of location metadata: 0=none, 1=approx, 2=exact
int json_aircraft_history_next; int json_aircraft_history_next;

View file

@ -22,8 +22,8 @@
// hash table size, must be a power of two: // hash table size, must be a power of two:
#define ICAO_FILTER_SIZE 4096 #define ICAO_FILTER_SIZE 4096
// Seconds between filter expiry flips: // Millis between filter expiry flips:
#define MODES_ICAO_FILTER_TTL 60 #define MODES_ICAO_FILTER_TTL 60000
// Open-addressed hash table with linear probing. // Open-addressed hash table with linear probing.
// We store each address twice to handle Data/Parity // We store each address twice to handle Data/Parity
@ -124,8 +124,8 @@ uint32_t icaoFilterTestFuzzy(uint32_t partial)
// call this periodically: // call this periodically:
void icaoFilterExpire() void icaoFilterExpire()
{ {
static time_t next_flip = 0; static uint64_t next_flip = 0;
time_t now = time(NULL); uint64_t now = mstime();
if (now >= next_flip) { if (now >= next_flip) {
if (icao_filter_active == icao_filter_a) { if (icao_filter_active == icao_filter_a) {

View file

@ -127,7 +127,7 @@ void modesInitNet(void) {
services[j].writer->socket = s; services[j].writer->socket = s;
services[j].writer->connections = 0; services[j].writer->connections = 0;
services[j].writer->dataUsed = 0; services[j].writer->dataUsed = 0;
services[j].writer->lastWrite = time(NULL); services[j].writer->lastWrite = mstime();
} }
} else { } else {
if (Modes.debug & MODES_DEBUG_NET) printf("%s port is disabled\n", services[j].descr); if (Modes.debug & MODES_DEBUG_NET) printf("%s port is disabled\n", services[j].descr);
@ -165,7 +165,7 @@ struct client * modesAcceptClients(void) {
if (services[j].writer) { if (services[j].writer) {
if (++ services[j].writer->connections == 1) { if (++ services[j].writer->connections == 1) {
services[j].writer->lastWrite = time(NULL); // suppress heartbeat initially services[j].writer->lastWrite = mstime(); // suppress heartbeat initially
} }
} }
@ -229,7 +229,7 @@ static void flushWrites(struct net_writer *writer) {
} }
writer->dataUsed = 0; writer->dataUsed = 0;
writer->lastWrite = time(NULL); writer->lastWrite = mstime();
} }
// Prepare to write up to 'len' bytes to the given net_writer. // Prepare to write up to 'len' bytes to the given net_writer.
@ -954,9 +954,9 @@ char *generateReceiverJson(const char *url_path, int *len)
p += sprintf(p, "{ " \ p += sprintf(p, "{ " \
"\"version\" : \"%s\", " "\"version\" : \"%s\", "
"\"refresh\" : %d, " "\"refresh\" : %.0f, "
"\"history\" : %d", "\"history\" : %d",
MODES_DUMP1090_VERSION, Modes.json_interval * 1000, history_size); MODES_DUMP1090_VERSION, 1.0*Modes.json_interval, history_size);
if (Modes.json_location_accuracy && (Modes.fUserLat != 0.0 || Modes.fUserLon != 0.0)) { if (Modes.json_location_accuracy && (Modes.fUserLat != 0.0 || Modes.fUserLon != 0.0)) {
if (Modes.json_location_accuracy == 1) { if (Modes.json_location_accuracy == 1) {
@ -1541,7 +1541,7 @@ static void writeFATSV() {
// //
void modesNetPeriodicWork(void) { void modesNetPeriodicWork(void) {
struct client *c, **prev; struct client *c, **prev;
time_t now = time(NULL); uint64_t now = mstime();
int j; int j;
int need_heartbeat = 0, need_flush = 0; int need_heartbeat = 0, need_flush = 0;

View file

@ -59,6 +59,7 @@ void add_timespecs(const struct timespec *x, const struct timespec *y, struct ti
void display_stats(struct stats *st) { void display_stats(struct stats *st) {
int j; int j;
time_t tt_start, tt_end;
struct tm tm_start, tm_end; struct tm tm_start, tm_end;
char tb_start[30], tb_end[30]; char tb_start[30], tb_end[30];
@ -66,9 +67,11 @@ void display_stats(struct stats *st) {
if (Modes.interactive) if (Modes.interactive)
interactiveShowData(); interactiveShowData();
localtime_r(&st->start, &tm_start); tt_start = st->start/1000;
localtime_r(&tt_start, &tm_start);
strftime(tb_start, sizeof(tb_start), "%c", &tm_start); strftime(tb_start, sizeof(tb_start), "%c", &tm_start);
localtime_r(&st->end, &tm_end); tt_end = st->end/1000;
localtime_r(&tt_end, &tm_end);
strftime(tb_end, sizeof(tb_end), "%c", &tm_end); strftime(tb_end, sizeof(tb_end), "%c", &tm_end);
printf("Statistics: %s - %s\n", tb_start, tb_end); printf("Statistics: %s - %s\n", tb_start, tb_end);
@ -147,7 +150,7 @@ void display_stats(struct stats *st) {
" %llu ms for demodulation\n" " %llu ms for demodulation\n"
" %llu ms for reading from USB\n" " %llu ms for reading from USB\n"
" %llu ms for network input and background tasks\n", " %llu ms for network input and background tasks\n",
0.1 * (demod_cpu_millis + reader_cpu_millis + background_cpu_millis) / (st->end - st->start + 1), 100.0 * (demod_cpu_millis + reader_cpu_millis + background_cpu_millis) / (st->end - st->start + 1),
(unsigned long long) demod_cpu_millis, (unsigned long long) demod_cpu_millis,
(unsigned long long) reader_cpu_millis, (unsigned long long) reader_cpu_millis,
(unsigned long long) background_cpu_millis); (unsigned long long) background_cpu_millis);

View file

@ -51,8 +51,8 @@
#define DUMP1090_STATS_H #define DUMP1090_STATS_H
struct stats { struct stats {
time_t start; uint64_t start;
time_t end; uint64_t end;
// Mode S demodulator counts: // Mode S demodulator counts:
uint32_t demod_preambles; uint32_t demod_preambles;

View file

@ -171,40 +171,6 @@ void showHelp(void) {
); );
} }
#ifdef _WIN32
void showCopyright(void) {
uint64_t llTime = time(NULL) + 1;
printf(
"-----------------------------------------------------------------------------\n"
"| view1090 ModeS Viewer Ver : " MODES_DUMP1090_VERSION " |\n"
"-----------------------------------------------------------------------------\n"
"\n"
" Copyright (C) 2012 by Salvatore Sanfilippo <antirez@gmail.com>\n"
" Copyright (C) 2014 by Malcolm Robb <support@attavionics.com>\n"
"\n"
" All rights reserved.\n"
"\n"
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
" ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
" HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
"\n"
" For further details refer to <https://github.com/MalcolmRobb/dump1090>\n"
"\n"
);
// delay for 1 second to give the user a chance to read the copyright
while (llTime >= time(NULL)) {}
}
#endif
// //
//========================================================================= //=========================================================================
// //