aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore11
-rw-r--r--Makefile12
-rw-r--r--include/libsurvive/survive.h5
-rwxr-xr-xsrc/survive.c8
-rwxr-xr-xsrc/survive_cal.c2
-rw-r--r--src/survive_data.c47
-rwxr-xr-xsrc/survive_vive.c62
-rw-r--r--useful_files/lib_survive_logo3.svg236
8 files changed, 310 insertions, 73 deletions
diff --git a/.gitignore b/.gitignore
index cb8f780..a7db3ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,14 @@ windows/calibrate.exe
windows/calinfo/1.json.gz
windows/calibrate.def
windows/calinfo/3.json.gz
+windows/calinfo/WM1_points.csv
+windows/calinfo/HMD_normals.csv
+windows/calinfo/WM0_points.csv
+windows/calinfo/WM1_normals.csv
+windows/calinfo/HMD_points.csv
+windows/calinfo/WM0_normals.csv
+windows/calinfo/2.json.gz
+windows/calinfo - Copy/WM1_points.csv
+windows/udev[USB_DEV_LIGHTHOUSE]
+windows/udev[USB_DEV_LIGHTHOUSE]
+windows/config.json
diff --git a/Makefile b/Makefile
index 19f7c40..525f7c5 100644
--- a/Makefile
+++ b/Makefile
@@ -2,15 +2,25 @@ all : lib data_recorder test calibrate calibrate_client
CC:=gcc
-CFLAGS:=-Iinclude/libsurvive -I. -fPIC -g -O0 -Iredist -flto -DUSE_DOUBLE -std=gnu99 -rdynamic
+CFLAGS:=-Iinclude/libsurvive -I. -fPIC -g -O3 -Iredist -flto -DUSE_DOUBLE -std=gnu99 -rdynamic
LDFLAGS:=-lpthread -lusb-1.0 -lz -lX11 -lm -flto -g
POSERS:=src/poser_dummy.o src/poser_daveortho.o src/poser_charlesslow.o
REDISTS:=redist/json_helpers.o redist/linmath.o redist/jsmn.o
LIBSURVIVE_CORE:=src/survive.o src/survive_usb.o src/survive_data.o src/survive_process.o src/ootx_decoder.o src/survive_driverman.o src/survive_vive.o src/survive_config.o src/survive_cal.o
+LIBSURVIVE_CORE:=$(LIBSURVIVE_CORE)
LIBSURVIVE_O:=$(POSERS) $(REDISTS) $(LIBSURVIVE_CORE)
LIBSURVIVE_C:=$(LIBSURVIVE_O:.o=.c)
+#Useful Preprocessor Directives:
+# -DUSE_DOUBLE = use double instead of float for most operations.
+# -DNOZLIB = use puff.c
+# -DTCC = various things needed for TCC.
+# -DWINDOWS -DWIN32 = Building for Windows
+# -DHIDAPI = Build vive driver to use USBHID instead of interrupt/control messages.
+# -DRUNTIME_SYMNUM = Don't assume __attribute__((constructor)) works. Instead comb for anything starting with REGISTER.
+
+
GRAPHICS_LOFI:=redist/DrawFunctions.o redist/XDriver.o
# unused: redist/crc32.c
diff --git a/include/libsurvive/survive.h b/include/libsurvive/survive.h
index eb30252..e3e167a 100644
--- a/include/libsurvive/survive.h
+++ b/include/libsurvive/survive.h
@@ -50,8 +50,8 @@ struct SurviveObject
int8_t oldcode;
int8_t sync_set_number; //0 = master, 1 = slave, -1 = fault.
int8_t did_handle_ootx; //If unset, will send lightcap data for sync pulses next time a sensor is hit.
- uint32_t last_time[NUM_LIGHTHOUSES];
- uint32_t last_length[NUM_LIGHTHOUSES];
+ uint32_t last_sync_time[NUM_LIGHTHOUSES];
+ uint32_t last_sync_length[NUM_LIGHTHOUSES];
uint32_t recent_sync_time;
uint32_t last_lighttime; //May be a 24- or 32- bit number depending on what device.
@@ -161,7 +161,6 @@ void survive_add_driver( SurviveContext * ctx, void * payload, DeviceDriverCb po
typedef struct
{
uint8_t sensor_id;
- uint8_t type; //Mostly unused. Set to 255 to ignore it.
uint16_t length;
uint32_t timestamp;
}
diff --git a/src/survive.c b/src/survive.c
index 9d0ef01..81c45c3 100755
--- a/src/survive.c
+++ b/src/survive.c
@@ -66,7 +66,8 @@ SurviveContext * survive_init( int headless )
ctx->lh_config = malloc( sizeof(config_group) * NUM_LIGHTHOUSES);
init_config_group(ctx->global_config_values,10);
- init_config_group(ctx->lh_config,10);
+ init_config_group(&ctx->lh_config[0],10);
+ init_config_group(&ctx->lh_config[1],10);
config_read(ctx, "config.json");
@@ -263,9 +264,10 @@ struct SurviveObject * survive_get_so_by_name( struct SurviveContext * ctx, cons
int survive_simple_inflate( struct SurviveContext * ctx, const char * input, int inlen, char * output, int outlen )
{
+ //Tricky: we actually get 2 bytes of data on the front. I don't know what it's for. 0x78 0x9c - puff doesn't deal with it well.
unsigned long ol = outlen;
- unsigned long il = inlen;
- int ret = puff( output, &ol, input, &il );
+ unsigned long il = inlen-2;
+ int ret = puff( output, &ol, input+2, &il );
if( ret == 0 )
return ol;
else
diff --git a/src/survive_cal.c b/src/survive_cal.c
index 0ea9337..985ab24 100755
--- a/src/survive_cal.c
+++ b/src/survive_cal.c
@@ -267,7 +267,7 @@ void survive_cal_angle( struct SurviveObject * so, int sensor_id, int acode, uin
}
if( sensors_visible < MIN_SENSORS_VISIBLE_PER_LH_FOR_CAL )
{
- printf( "Dev %d, LH %d not enough visible points found.\n", i, j );
+ //printf( "Dev %d, LH %d not enough visible points found.\n", i, j );
cd->found_common = 0;
return;
}
diff --git a/src/survive_data.c b/src/survive_data.c
index f87c69d..60849e2 100644
--- a/src/survive_data.c
+++ b/src/survive_data.c
@@ -25,10 +25,10 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
//The sync pulse finder is taking Charles's old disambiguator code and mixing it with a more linear
//version of Julian Picht's disambiguator, available in 488c5e9. Removed afterwards into this
//unified driver.
- int ssn = so->sync_set_number;
+ int ssn = so->sync_set_number; //lighthouse number
if( ssn < 0 ) ssn = 0;
- int last_sync_time = so->last_time [ssn];
- int last_sync_length = so->last_length[ssn];
+ int last_sync_time = so->last_sync_time [ssn];
+ int last_sync_length = so->last_sync_length[ssn];
int32_t delta = le->timestamp - last_sync_time; //Handle time wrapping (be sure to be int32)
if( delta < -so->pulsedist_max_ticks || delta > so->pulsedist_max_ticks )
@@ -36,6 +36,7 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
//Reset pulse, etc.
so->sync_set_number = -1;
delta = so->pulsedist_max_ticks;
+// return; //if we don't know what lighthouse this is we don't care to do much else
}
@@ -43,6 +44,8 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
{
int is_new_pulse = delta > so->pulselength_min_sync /*1500*/ + last_sync_length;
+ printf("m sync %d %d %d %d\n", le->sensor_id, so->last_sync_time[ssn], le->timestamp, delta);
+
so->did_handle_ootx = 0;
if( is_new_pulse )
@@ -52,8 +55,8 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
if( is_master_sync_pulse )
{
ssn = so->sync_set_number = 0;
- so->last_time[ssn] = le->timestamp;
- so->last_length[ssn] = le->length;
+ so->last_sync_time[ssn] = le->timestamp;
+ so->last_sync_length[ssn] = le->length;
}
else if( so->sync_set_number == -1 )
{
@@ -69,8 +72,8 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
}
else
{
- so->last_time[ssn] = le->timestamp;
- so->last_length[ssn] = le->length;
+ so->last_sync_time[ssn] = le->timestamp;
+ so->last_sync_length[ssn] = le->length;
}
}
}
@@ -79,10 +82,10 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
//Find the longest pulse.
if( le->length > last_sync_length )
{
- if( so->last_time[ssn] > le->timestamp )
+ if( so->last_sync_time[ssn] > le->timestamp )
{
- so->last_time[ssn] = le->timestamp;
- so->last_length[ssn] = le->length;
+ so->last_sync_time[ssn] = le->timestamp;
+ so->last_sync_length[ssn] = le->length;
}
}
}
@@ -93,8 +96,8 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
//See if this is a valid actual pulse.
else if( le->length < so->pulse_max_for_sweep && delta > so->pulse_in_clear_time && ssn >= 0 )
{
- int32_t dl = so->last_time[0];
- int32_t tpco = so->last_length[0];
+ int32_t dl = so->last_sync_time[0];
+ int32_t tpco = so->last_sync_length[0];
#if NUM_LIGHTHOUSES != 2
@@ -109,8 +112,8 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
int32_t acode_array[2] =
{
- (so->last_length[0]+main_divisor+50)/(main_divisor*2), //+50 adds a small offset and seems to help always get it right.
- (so->last_length[1]+main_divisor+50)/(main_divisor*2), //Check the +50 in the future to see how well this works on a variety of hardware.
+ (so->last_sync_length[0]+main_divisor+50)/(main_divisor*2), //+50 adds a small offset and seems to help always get it right.
+ (so->last_sync_length[1]+main_divisor+50)/(main_divisor*2), //Check the +50 in the future to see how well this works on a variety of hardware.
};
//XXX: TODO: Capture error count here.
@@ -125,11 +128,13 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
if( !so->did_handle_ootx )
{
- int32_t delta1 = so->last_time[0] - so->recent_sync_time;
- int32_t delta2 = so->last_time[1] - so->last_time[0];
- ctx->lightproc( so, -1, acode_array[0], delta1, so->last_time[0], so->last_length[0] );
- ctx->lightproc( so, -2, acode_array[1], delta2, so->last_time[1], so->last_length[1] );
- so->recent_sync_time = so->last_time[1];
+ int32_t delta1 = so->last_sync_time[0] - so->recent_sync_time;
+ int32_t delta2 = so->last_sync_time[1] - so->last_sync_time[0];
+
+ ctx->lightproc( so, -1, acode_array[0], delta1, so->last_sync_time[0], so->last_sync_length[0] );
+ ctx->lightproc( so, -2, acode_array[1], delta2, so->last_sync_time[1], so->last_sync_length[1] );
+
+ so->recent_sync_time = so->last_sync_time[1];
//Throw out everything if our sync pulses look like they're bad.
@@ -161,8 +166,8 @@ void handle_lightcap( SurviveObject * so, LightcapElement * le )
//SV_INFO( "Warning: got a slave marker but only got a master sync." );
//This happens too frequently. Consider further examination.
}
- dl = so->last_time[1];
- tpco = so->last_length[1];
+ dl = so->last_sync_time[1];
+ tpco = so->last_sync_length[1];
}
int32_t offset_from = le->timestamp - dl + le->length/2;
diff --git a/src/survive_vive.c b/src/survive_vive.c
index 116d18b..728c3c9 100755
--- a/src/survive_vive.c
+++ b/src/survive_vive.c
@@ -17,10 +17,10 @@
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
+#include <os_generic.h>
#include <malloc.h> // for alloca
#ifdef HIDAPI
-#include <os_generic.h>
#if defined(WINDOWS) || defined(WIN32) || defined (_WIN32)
#include <windows.h>
#undef WCHAR_MAX
@@ -60,9 +60,13 @@ const char * devnames[] = {
#define USB_DEV_WATCHMAN1 2
#define USB_DEV_WATCHMAN2 3
#define USB_DEV_TRACKER0 4
+
+#ifdef HIDAPI
#define USB_DEV_LIGHTHOUSEB 5
#define MAX_USB_DEVS 6
-
+#else
+#define MAX_USB_DEVS 5
+#endif
#define USB_IF_HMD 0
#define USB_IF_LIGHTHOUSE 1
@@ -401,6 +405,7 @@ int survive_usb_init( struct SurviveViveData * sv, struct SurviveObject * hmd, s
if( d == 0 )
{
+ printf( "!!%p %d %04x %04x %d\n", devnames[i], i, vid, pid, which );
SV_INFO( "Did not find device %s (%04x:%04x.%d)", devnames[i], vid, pid, which );
sv->udev[i] = 0;
continue;
@@ -478,36 +483,23 @@ int survive_vive_send_magic(struct SurviveContext * ctx, void * drv, int magic_c
if( turnon )
{
- //Magic from vl_magic.h, originally copywritten under LGPL.
- // * Copyright (C) 2013 Fredrik Hultin
- // * Copyright (C) 2013 Jakob Bornecrantz
-#if 0
- static uint8_t vive_magic_power_on[] = {
- 0x04, 0x78, 0x29, 0x38, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01,
- 0xa8, 0x0d, 0x76, 0x00, 0x40, 0xfc, 0x01, 0x05, 0xfa, 0xec, 0xd1, 0x6d, 0x00,
- 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x0d, 0x76, 0x00, 0x68, 0xfc,
- 0x01, 0x05, 0x2c, 0xb0, 0x2e, 0x65, 0x7a, 0x0d, 0x76, 0x00, 0x68, 0x54, 0x72,
- 0x00, 0x18, 0x54, 0x72, 0x00, 0x00, 0x6a, 0x72, 0x00, 0x00, 0x00, 0x00,
- };
-#else
//From actual steam.
- static uint8_t vive_magic_power_on[64] = { 0x04, 0x78, 0x29, 0x38,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-#endif
if (sv->udev[USB_DEV_HMD])
{
+ static uint8_t vive_magic_power_on[64] = { 0x04, 0x78, 0x29, 0x38 };
r = update_feature_report( sv->udev[USB_DEV_HMD], 0, vive_magic_power_on, sizeof( vive_magic_power_on ) );
if( r != sizeof( vive_magic_power_on ) ) return 5;
}
if (sv->udev[USB_DEV_LIGHTHOUSE])
{
- static uint8_t vive_magic_enable_lighthouse[64] = { 0x04 }; //[64] wat? Why did that fix it?
- r = update_feature_report( sv->udev[USB_DEV_LIGHTHOUSE], 0, vive_magic_enable_lighthouse, sizeof( vive_magic_enable_lighthouse ) ); ///XXX TODO: Shouldn't this be LIGHTHOUSEB for hidapi?
+ static uint8_t vive_magic_enable_lighthouse[5] = { 0x04 };
+ r = update_feature_report( sv->udev[USB_DEV_LIGHTHOUSE], 0, vive_magic_enable_lighthouse, sizeof( vive_magic_enable_lighthouse ) );
if( r != sizeof( vive_magic_enable_lighthouse ) ) return 5;
+
+ static uint8_t vive_magic_enable_lighthouse2[5] = { 0x07, 0x02 }; //Switch to 0x25 mode (able to get more light updates)
+ r = update_feature_report( sv->udev[USB_DEV_LIGHTHOUSE], 0, vive_magic_enable_lighthouse2, sizeof( vive_magic_enable_lighthouse2 ) );
+ if( r != sizeof( vive_magic_enable_lighthouse2 ) ) return 5;
}
#if 0
@@ -619,16 +611,7 @@ int survive_get_config( char ** config, struct SurviveViveData * sv, int devno,
int k;
- uint8_t cfgbuff_send[64] = {
- 0xff, 0x83, 0x00, 0xb6, 0x5b, 0xb0, 0x78, 0x69,
- 0x0f, 0xf8, 0x78, 0x69, 0x0f, 0xa0, 0xf3, 0x18,
- 0x00, 0xe8, 0xf2, 0x18, 0x00, 0x27, 0x44, 0x5a,
- 0x0f, 0xf8, 0x78, 0x69, 0x0f, 0xf0, 0x77, 0x69,
- 0x0f, 0xf0, 0x77, 0x69, 0x0f, 0x50, 0xca, 0x45,
- 0x77, 0xa0, 0xf3, 0x18, 0x00, 0xf8, 0x78, 0x69,
- 0x0f, 0x00, 0x00, 0xa0, 0x0f, 0xa0, 0x9b, 0x0a,
- 0x01, 0x00, 0x00, 0x35, 0x00, 0x34, 0x02, 0x00
- };
+ uint8_t cfgbuff_send[64] = { 0xff, 0x83 };
#ifdef HIDAPI
//XXX TODO WRITEME
@@ -709,7 +692,7 @@ int survive_get_config( char ** config, struct SurviveViveData * sv, int devno,
int len = survive_simple_inflate( ctx, compressed_data, count, uncompressed_data, sizeof(uncompressed_data)-1 );
if( len <= 0 )
{
- SV_INFO( "Error: data for config descriptor %d:%d is bad.", devno, iface );
+ SV_INFO( "Error: data for config descriptor %d:%d is bad. (%d)", devno, iface, len );
return -5;
}
@@ -924,7 +907,6 @@ static void handle_watchman( struct SurviveObject * w, uint8_t * readdata )
//Use insertion sort, since we should most of the time, be in order.
LightcapElement * le = &les[lese++];
le->sensor_id = led;
- le->type = 0xfe;
if( (uint32_t)(endtime - starttime) > 65535 ) { fault = 6; goto end; } //Length of pulse dumb.
le->length = endtime - starttime;
@@ -1078,24 +1060,16 @@ void survive_data_cb( SurviveUSBInterface * si )
case USB_IF_LIGHTCAP:
{
int i;
- #ifdef HIDAPI
- for( i = 0; i < 7; i++ )
+ for( i = 0; i < 9; i++ )
{
LightcapElement le;
le.sensor_id = POP1;
- le.type = 0xfe;
le.length = POP2;
le.timestamp = POP4;
if( le.sensor_id == 0xff ) break;
handle_lightcap( obj, &le );
}
- #else
- for( i = 0; i < 7; i++ )
- {
- handle_lightcap( obj, (LightcapElement*)&readdata[i*8] );
- }
break;
- #endif
}
}
}
@@ -1226,7 +1200,7 @@ printf( "Loading config: %d\n", len );
return 1;
}
- char fname[20];
+ char fname[64];
sprintf( fname, "calinfo/%s_points.csv", so->codename );
FILE * f = fopen( fname, "w" );
diff --git a/useful_files/lib_survive_logo3.svg b/useful_files/lib_survive_logo3.svg
new file mode 100644
index 0000000..a6d003e
--- /dev/null
+++ b/useful_files/lib_survive_logo3.svg
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="238.93333"
+ height="238.93333"
+ viewBox="0 0 210 210"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="lib_survive_logo2(1).svg"
+ inkscape:export-filename="lib_survive_logo_sm.png"
+ inkscape:export-xdpi="13.499999"
+ inkscape:export-ydpi="13.499999">
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4339">
+ <stop
+ style="stop-color:#ff0000;stop-opacity:1;"
+ offset="0"
+ id="stop4341" />
+ <stop
+ style="stop-color:#ff0000;stop-opacity:0;"
+ offset="1"
+ id="stop4343" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="304.07246 : 374.52182 : 1"
+ inkscape:vp_y="0 : 999.99998 : 0"
+ inkscape:vp_z="97.924826 : 490.48422 : 1"
+ inkscape:persp3d-origin="282.04724 : 396.50169 : 1"
+ id="perspective4191" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4339"
+ id="linearGradient4345"
+ x1="205.13866"
+ y1="791.02484"
+ x2="130.30968"
+ y2="856.89771"
+ gradientUnits="userSpaceOnUse" />
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath3493">
+ <rect
+ style="fill:#666666;fill-opacity:1;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3495"
+ width="146.20735"
+ height="147.45538"
+ x="115.33373"
+ y="738.79926"
+ ry="6.3883986" />
+ </clipPath>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="133.52724"
+ inkscape:cy="93.936777"
+ inkscape:document-units="px"
+ inkscape:current-layer="g4414"
+ showgrid="false"
+ fit-margin-left="5"
+ fit-margin-top="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="905"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:snap-global="false"
+ showguides="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(5.0000003,-847.36221)">
+ <g
+ id="g4433"
+ transform="translate(-60.902386,119.96554)">
+ <rect
+ ry="8.6648569"
+ y="732.39667"
+ x="60.902386"
+ height="200"
+ width="200"
+ id="rect4412"
+ style="fill:#666666;fill-opacity:1;stroke:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g4414"
+ transform="matrix(1.3679202,0,0,1.3563425,-96.864952,-269.66815)"
+ clip-path="url(#clipPath3493)">
+ <g
+ id="g4364"
+ transform="matrix(1.2848586,0,0,1.2848586,-47.09026,-225.40973)">
+ <g
+ transform="matrix(0.70521296,0.07124105,0,0.70521296,22.935416,485.79994)"
+ id="g4326">
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.35"
+ id="rect4277"
+ width="48.565189"
+ height="50.670372"
+ x="242.52609"
+ y="315.56894"
+ ry="12.130823"
+ transform="matrix(0.97464031,0.22377727,0,1,0,0)" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.35"
+ id="rect4277-7"
+ width="48.565189"
+ height="50.670372"
+ x="262.80905"
+ y="292.12814"
+ ry="12.130823"
+ transform="matrix(0.97464031,0.22377727,0,1,0,0)" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.35"
+ id="rect4294"
+ width="29.069714"
+ height="15.917919"
+ x="-73.198318"
+ y="437.03619"
+ ry="0"
+ transform="matrix(0.7329244,-0.68031009,0.66822927,0.7439554,0,0)" />
+ <rect
+ style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.35"
+ id="rect4294-3"
+ width="27.907652"
+ height="15.532421"
+ x="-54.329411"
+ y="487.7229"
+ ry="0"
+ transform="matrix(0.70821685,-0.70599496,0.63527567,0.77228545,0,0)" />
+ <rect
+ style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.37930703;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect4277-78"
+ width="47.474747"
+ height="49.333805"
+ x="243.31599"
+ y="316.28986"
+ ry="11.810841"
+ transform="matrix(0.97494341,0.22245301,0,1,0,0)" />
+ </g>
+ <path
+ sodipodi:nodetypes="czcc"
+ inkscape:connector-curvature="0"
+ id="path4337"
+ d="m 84.852815,819.52204 c 0,0 111.599975,-40.64046 121.766705,-32.01632 10.16673,8.62414 -17.97354,123.43513 -17.97354,123.43513 z"
+ style="fill:url(#linearGradient4345);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <g
+ id="g4377-2"
+ style="fill:none;stroke:#00ff00;stroke-width:2.54694843;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.6856173,0,0,1.6856173,-39.282189,-597.66653)">
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4347-3"
+ d="m 106.31855,858.4553 15,0"
+ style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.54694843;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4347-5-21"
+ d="m 113.81855,850.73652 0,15"
+ style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.54694843;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g4377-2-3"
+ style="fill:none;stroke:#00ff00;stroke-width:2.54694843;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.6856173,0,0,1.6856173,-15.550497,-631.83198)">
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4347-3-6"
+ d="m 106.31855,858.4553 15,0"
+ style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.54694843;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4347-5-21-7"
+ d="m 113.81855,850.73652 0,15"
+ style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.54694843;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g4377-2-5"
+ style="fill:none;stroke:#00ff00;stroke-width:2.54694843;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.6856173,0,0,1.6856173,8.7357605,-597.93633)">
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4347-3-3"
+ d="m 106.31855,858.4553 15,0"
+ style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.54694843;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4347-5-21-5"
+ d="m 113.81855,850.73652 0,15"
+ style="fill:none;fill-rule:evenodd;stroke:#00ff00;stroke-width:2.54694843;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>