'Analysis tool for movement data recorded by "Movement_record" function in gestrobo 'we try computing movement repetition freq ' > select one buffer or a whole range of buffers and analyses movement repetition frequency 'file format: chr$(0, 0) = escape - frollowed by 5 byte string: ' esc + "DATA:" + chr$(13, 10) = beginning of data section ' data section = dump of one dim of databuffers (first el = ptr, never 0; hence 00 as escape) ' esc + "COMM:" + string$ + chr$(13, 10) (string$ = actual comment - read until carriage return) ' esc + "FREQ:" + frequency AS LONG (= freq of repetitive movement) 'note: datasection can be interrupted after each dumped type ' double dataheaders may occur ' we presume that samplingrate = 64 '031223 - kl 'correlation with square wave on the x acceleration channel gives promising results: 'with explicit rythmic movement, the two peek frequencies for a 512 or 256 buffer are usually : ' or the right repetition frequency and its double, ' or the two frequencies closest to the right frequency (if the frequency itself is in between two bins) 'when examining a large number of buffers this becomes even more clear 'we need 512 or 256 buffers; 128 was not enough! 'now we should test with less 'clear' signals -> first test looked ok 'see what buffer size we actually need and how much buffers we should take into account.. 256?? '!! something to check for further implementation: ' when seeing lots of buffers pass, usually we see one/two high peeks ' ever so often those disappear completely in the noise ' so, if peek amplitude drops suddenly, maybe it's better to stay at constant freq ' so we take moving average of peek(s) of last n buffers, on sudden drop wait a bit to follow then jump (?? - try it..) ' also for halving/doubling frequency we better stay constant '040130 'weighted average of strongest peeks seems to work good 'results over small numbel of samples is somewhat less reliable > in realtime we will need to integrate rather slowly 'for low frequencies often the double freq is returned as strongest.. #INCLUDE "c:\b\pb\winapi\win32api.inc" #INCLUDE "c:\b\pb\winapi\comdlg32.inc" #INCLUDE "c:\b\pb\gmt\g_kons.bi" #INCLUDE "c:\b\pb\gmt\g_type.bi" TYPE MovementEntryType comment AS STRING * 1000 'comments for this block freq AS SINGLE 'expected movement frequency for this block seekpos AS DWORD 'we rtemember pos of data chunk in file, we read it when needed.. nrfields AS DWORD END TYPE GLOBAL ntry() AS MovementEntryType GLOBAL f AS LONG DECLARE FUNCTION ShowSonarData(xac() AS INTEGER, BYVAL dimn AS LONG) AS LONG DECLARE FUNCTION CreateControlWindow AS LONG DECLARE FUNCTION Analyzedata(xac() AS INTEGER) AS LONG DECLARE FUNCTION summary(pA AS LONG PTR, OPT BYVAL rawflag AS LONG) AS STRING DECLARE FUNCTION Parsemov_FileOpenName(title$) AS STRING DECLARE SUB ShowHistograms (sph() AS SINGLE, aph() AS SINGLE, mph() AS SINGLE) DECLARE CALLBACK FUNCTION cbanawin FUNCTION PBMAIN ' LOCAL f AS LONG LOCAL i AS LONG LOCAL freq AS SINGLE LOCAL b$ LOCAL buf$ LOCAL fnam$ LOCAL sr() AS sonartype LOCAL ec AS DWORD 'entry count DIM sr(1000) DIM ntry(100) DIM xac(255) AS LOCAL INTEGER 'read data file f = FREEFILE ' fnam$ = INPUTBOX$("Datafile:",FUNCNAME$,"c:\b\pb\gmt\movementdata_"+DATE$+".dat") fnam$ = ParseMov_fileopenname("Datafile:") OPEN fnam$ FOR BINARY ACCESS READ LOCK WRITE AS f IF ERRCLEAR THEN MSGBOX "failed opening " + fnam$ EXIT FUNCTION END IF ERRCLEAR readnextpart: i = SEEK(f) GET$ f, 7, b$ IF EOF(f) THEN MSGBOX "end of file" DECR ec REDIM PRESERVE ntry(ec) GOTO mainloop END IF IF LEFT$(b$, 2) <> CHR$(0,0) THEN i = SEEK(f) REPLACE CHR$(0) WITH "/0" IN b$ MSGBOX "error reading data file " + fnam$ + " at position" + STR$(i) + $CRLF + "incorrect field: <" + b$ + ">" MSGBOX "text buffer at this point was:" + $CRLF + buf$ GOTO mainloop EXIT FUNCTION END IF SELECT CASE RIGHT$(b$, 5) CASE "COMM:" 'read comment until escape (crh$(0,0)) buf$ = REMOVE$(TRIM$(ntry(ec).comment), CHR$(0)) DO GET$ f, 2, b$ IF b$ = CHR$(0, 0) THEN i = SEEK(f) SEEK f, (SEEK(f) - 2) i = SEEK(f) ntry(ec).comment = buf$ GOTO readnextpart END IF buf$ = buf$ + b$ LOOP CASE "FREQ:" GET f, ,freq ntry(ec).freq = freq GOTO readnextpart CASE "DATA:" GET$ f, 7, b$ SEEK f, SEEK(f) - 7 IF LEFT$(b$, 2) = CHR$(0, 0) THEN IF RIGHT$(b$, 5) = "COMM:" OR RIGHT$(b$, 5) = "FREQ:" OR RIGHT$(b$, 5) = "DATA:" THEN GOTO readnextpart 'as empty data parts are allowed.. END IF ntry(ec).seekpos = SEEK(f) i = 0 DO UNTIL EOF(f) GET$ f, 7, b$ SEEK f,SEEK(f) - 7 IF LEFT$(b$, 2) = CHR$(0, 0) THEN IF RIGHT$(b$, 5) = "COMM:" OR RIGHT$(b$, 5) = "FREQ:" OR RIGHT$(b$, 5) = "DATA:" THEN EXIT LOOP 'as empty data parts are allowed.. END IF GET f, ,xac() INCR i LOOP DECR i ntry(ec).nrfields = i MSGBOX "ready sonardata@" + fnam$ + $CRLF + STR$(i) + " records" + $CRLF + "meta data for this section:" + $CRLF + ntry(ec).comment + $CRLF + "freq:" + STR$(ntry(ec).freq) i = SEEK(f) ShowSonarData xac(), 0 INCR ec IF ec > UBOUND(ntry) THEN REDIM PRESERVE ntry(UBOUND(ntry) + 10) GOTO readnextpart CASE ELSE REPLACE CHR$(0) WITH "\0" IN b$ MSGBOX "unexpected field:" + b$ GOTO readnextpart END SELECT mainloop: CreateControlWindow DO: DIALOG DOEVENTS TO i: LOOP WHILE i CLOSE f END FUNCTION FUNCTION CreateControlWindow AS LONG 'creates win from where we can choose data section to examine, buffers from section to show/analyse etc.. STATIC hw AS LONG LOCAL i AS LONG LOCAL b$ LOCAL field() AS STRING * 5 IF ISFALSE hw THEN DIALOG NEW 0, "control room",,,208,150 TO hw CONTROL ADD LISTBOX, hw, 1,,1,1, 130, 70, %WS_VSCROLL OR %WS_HSCROLL OR %LBS_NOTIFY OR %WS_TABSTOP FOR i = 0 TO UBOUND(ntry) b$ = TRIM$(STR$(i)) + ". " + TRIM$(ntry(i).comment) + "(fr:" + STR$(ntry(i).freq)+")" REPLACE $CRLF WITH " / " IN b$ LISTBOX ADD hw, 1, b$ NEXT CONTROL ADD TEXTBOX, hw, 10, "0", 1, 73, 50, 12 CONTROL ADD LABEL, hw, 11, "/" + STR$(ntry(0).nrfields), 52, 75, 50, 12 CONTROL ADD BUTTON, hw, 12, "Show", 104, 75, 50, 12 CONTROL ADD BUTTON, hw, 13, "Analyze", 156, 75, 50, 12 CONTROL ADD TEXTBOX, hw, 50, "0", 1, 89, 50, 12 CONTROL ADD TEXTBOX, hw, 51, "2", 52, 89, 50, 12 CONTROL ADD BUTTON, hw, 55, "RunThrough", 104, 89, 50, 12 ' control add label, hw, 100, "Zoom:", 133, 1, 30, 12 CONTROL ADD TEXTBOX, hw, 20, "select a sample to analyze and press the analyze button", 1,104, 206, 36, %ES_MULTILINE OR %ES_READONLY OR %WS_BORDER DIALOG SHOW MODELESS hw CALL CbControlwindow END IF END FUNCTION CALLBACK FUNCTION CbControlWindow LOCAL b$ LOCAL i AS LONG LOCAL pA AS LONG PTR LOCAL pseek AS LONG DIM xac(511) AS LOCAL INTEGER STATIC currententry AS LONG SELECT CASE CBMSG CASE %WM_COMMAND SELECT CASE CBCTLMSG CASE %LBN_DBLCLK IF CBCTL <> 1 THEN EXIT FUNCTION LISTBOX GET TEXT CBHNDL, CBCTL TO b$ i = VAL(b$) currententry = i CONTROL SET TEXT CBHNDL, 11, "/" + STR$(ntry(i).nrfields) DIALOG SET TEXT CBHNDL, "control room - " + b$ CASE %BN_CLICKED SELECT CASE CBCTL CASE 12 'show data CONTROL GET TEXT CBHNDL, 10 TO b$ i = MIN(ntry(currententry).nrfields - 1, VAL(b$)) CONTROL SET TEXT CBHNDL, 10, STR$(i) pseek = ntry(currententry).seekpos + 512 * i SEEK f, pseek ' ' ' redim xac(80) GET f, ,xac() ShowSonarData xac(), 0 CASE 13 'analyse CONTROL GET TEXT CBHNDL, 10 TO b$ i = MIN(ntry(i).nrfields - 1, VAL(b$)) CONTROL SET TEXT CBHNDL, 10, STR$(i) pseek = ntry(currententry).seekpos + 512 * i SEEK f, pseek GET f, ,xac() pA = (Analyzedata xac()) b$ = Summary(pA) CONTROL SET TEXT CBHNDL, 20, b$ CASE 55 'runthrough button DIM slowpeekhist(300) AS LOCAL SINGLE 'freq of first peek histogram DIM avgpeekhist(300) AS LOCAL SINGLE 'freq of first peek that is higher than average freq val histo DIM maxpeekhist(300) AS LOCAL SINGLE 'freq ofhighest peek histo LOCAL smax AS SINGLE, amax AS SINGLE, mmax AS SINGLE CONTROL GET TEXT CBHNDL, 50 TO b$ i = MIN(ABS(VAL(b$)), ntry(currententry).nrfields - 1) pseek = ntry(currententry).seekpos + 512 * i CONTROL GET TEXT CBHNDL, 51 TO b$ i = MIN(ABS(VAL(b$)), ntry(currententry).nrfields - 1) i = ntry(currententry).seekpos + 512 * i DO SEEK f, pseek GET f,,xac() pA = Analyzedata xac() b$ = summary(pA, 1) 'returns bins of lowest peek, lowest above average, max INCR slowpeekhist(5 * VAL(PARSE$(b$, ",", 1))) '*5 = only to separate samples visually!! smax = MAX(smax, slowpeekhist(5 * VAL(PARSE$(b$, ",", 1)))) INCR avgpeekhist(5 * VAL(PARSE$(b$, ",", 2))) amax = MAX(amax, avgpeekhist(5 * VAL(PARSE$(b$, ",", 2)))) INCR maxpeekhist(5 * VAL(PARSE$(b$, ",", 3))) mmax = MAX(mmax, maxpeekhist(5 * VAL(PARSE$(b$, ",", 3)))) pseek = pseek + 2 'was 64, must be even nr; 512/n IF pseek >= i THEN EXIT LOOP LOOP smax = MAX(smax, amax, mmax) smax = 1/smax MAT slowpeekhist() = (smax) * slowpeekhist() MAT avgpeekhist() = (smax) * avgpeekhist() MAT maxpeekhist() = (smax) * maxpeekhist() ShowHistograms slowpeekhist(), avgpeekhist(), maxpeekhist() b$ = "Mins:" FOR i = 0 TO UBOUND(slowpeekhist) IF slowpeekhist(i) > .2 THEN b$ = b$ + STR$(i/80) + "Hz ("+TRIM$(STR$(slowpeekhist(i))) + ")" NEXT b$ = b$ + $CRLF + "Mids:" FOR i = 0 TO UBOUND(avgpeekhist) IF avgpeekhist(i) > .2 THEN b$ = b$ + STR$(i/80)+ "Hz ("+TRIM$(STR$(avgpeekhist(i))) + ")" NEXT LOCAL estfreq AS SINGLE, divpeek AS SINGLE DIM peeks(UBOUND(maxpeekhist)) AS LOCAL SINGLE: DIM peekvals(UBOUND(maxpeekhist)) AS LOCAL SINGLE b$ = b$ + $CRLF + "Peeks:" FOR i = 0 TO UBOUND(maxpeekhist) IF maxpeekhist(i) > .2 THEN b$ = b$ + STR$(i/80)+ "Hz, ("+TRIM$(STR$(maxpeekhist(i))) + ")" peeks(i) = i/80 peekvals(i) = maxpeekhist(i) END IF NEXT ARRAY SORT peekvals(), TAGARRAY peeks(), DESCEND estfreq = peeks(0) 'if other peeks are close, take average, taking in account strength of peek IF peekvals(0) / 2 < peekvals(1) THEN divpeek = peekvals(0) estfreq = estfreq * peekvals(0) FOR i = 1 TO UBOUND(peekvals) IF peekvals(0) / 2 > peekvals(i) THEN EXIT FOR estfreq = estfreq + peeks(i) * peekvals(i) divpeek = divpeek + peekvals(i) NEXT estfreq = estfreq / divpeek END IF b$ = b$ + $CRLF + "estimated frequency:" + STR$(estfreq) MSGBOX b$ END SELECT END SELECT END SELECT END FUNCTION FUNCTION ShowSonarData(arr() AS INTEGER, BYVAL dimn AS LONG) AS LONG 'shows one buffer of sonar data in one dim STATIC hw AS LONG LOCAL sp() AS SINGLE LOCAL oldpoint AS POINTL LOCAL v% LOCAL h!, horstep! LOCAL i AS DWORD LOCAL hDC AS LONG LOCAL Rechthoek AS FourLongs LOCAL ver% DIM sp(UBOUND(arr)) IF ISFALSE hw THEN DIALOG NEW 0, "SonarData",,,400,200 TO hw: DIALOG SHOW MODELESS hw hDC = GetDC(hW) GetClientRect hW, Rechthoek ver% = Rechthoek.h /2 ' erase previous graph PatBlt hDC, Rechthoek.x,0,Rechthoek.b,Rechthoek.h,%WHITENESS horstep! = Rechthoek.b / UBOUND(Arr) MoveToEx hDC, rechthoek.x,ver%, oldpoint: LineTo hDC, rechthoek.x + rechthoek.b,ver% MoveToEx hDc, rechthoek.x + rechthoek.b /2,rechthoek.y, oldpoint: LineTo hDc, rechthoek.x + rechthoek.b/2, rechthoek.y + rechthoek.h FOR i = 0 TO UBOUND(arr): sp(i) = (arr(i) / %d11): NEXT h! = Rechthoek.x FOR i = 0 TO UBOUND(sp) v% = ver% - (sp(i) * rechthoek.h) IF ISFALSE i THEN MoveToEx hDC, INT(h!),v%, oldpoint ELSE MoveToEx hDC, INT(h!), ver%, oldpoint LineTo hDC, INT(h!),v% END IF h! = h! + horstep! NEXT i ReleaseDC hW,hDC FUNCTION = hw END FUNCTION FUNCTION Analyzedata(xac() AS INTEGER) AS LONG LOCAL v AS LONG STATIC minv AS LONG STATIC hw AS LONG ' static maxv as long LOCAL j AS LONG LOCAL i AS LONG LOCAL k AS LONG LOCAL fase AS LONG LOCAL p AS INTEGER POINTER STATIC init AS LONG LOCAL sp() AS SINGLE LOCAL avg AS SINGLE LOCAL oldpoint AS POINTL LOCAL h!, horstep! LOCAL hDC AS LONG LOCAL Rechthoek AS FourLongs LOCAL ver% 'try extratcting tempo from correlation. 'sr = 64 samples/second 'correlation with 00000001000000 seemed not very reliable when analyzing one chunk 'analysing of lots of chunks resulted in he right frequency plus stronger! subharmonics 'now we try to correlate with a five sample sawtooth ramp up DIM corval(128) AS LOCAL LONG DIM sp(128) FOR j = 128 TO 6 STEP -1 'drop lowest.. nt significant... (?) fase = 0: v = 0 DO v = 0 FOR i = fase TO 256 - 4 STEP 512/j '256 & 512 work well!! 128 is not enough periode = 512/j, samplingrate 64 'square - THIS WORKS WELL!! FOR k = 0 TO (512/j)/2 v = v + xac(i + k) v = v - xac(i + 2 * k) NEXT ' previous attempts.. ' 'sawtooth rampup ' v = v + 0.0666667 * xac(i-4) + 0.1333333 * xac(i-3) + _ ' 0.2 * xac(i-2) + 0.2666667 * xac(i-1) + 0.3333333 * xac(i) ' 'kwart sinus ' v = v + .114 * xac(i-4) + .223 * xac(i-3) + .303 * xac(i-2) + .36 * xac(i-1) + .379 * xac(i) ' 'spike ' v = v + xac(i) NEXT 'try to adapt this to 1 period of freq we look at v = v / j IF v > corval(j) THEN corval(j) = v INCR fase LOOP UNTIL fase > j NEXT j = 0: v = 0 FOR i = 2 TO 128 IF corval(i) > j THEN j = corval(i): v = i NEXT 'if j < some min (2000??)then disregard - corval seems to b 8000 at max 'show analysis IF ISFALSE hw THEN DIALOG NEW 0, "Analysis",,,400,150 TO hw: DIALOG SHOW MODELESS hw CALL cbanawin hDC = GetDC(hW) GetClientRect hW, Rechthoek ver% = Rechthoek.h '/2 PatBlt hDC, Rechthoek.x,0,Rechthoek.b,Rechthoek.h,%WHITENESS horstep! = Rechthoek.b / UBOUND(corval) MoveToEx hDC, rechthoek.x,ver%, oldpoint: LineTo hDC, rechthoek.x + rechthoek.b,ver% MoveToEx hDc, rechthoek.x + rechthoek.b /2,rechthoek.y, oldpoint: LineTo hDc, rechthoek.x + rechthoek.b/2, rechthoek.y + rechthoek.h FOR i = 0 TO UBOUND(corval): sp(i) = (corval(i) / 900): avg = avg + sp(i): NEXT avg = avg / (UBOUND(corval) + 1) v = ver% - avg * rechthoek.h MoveToEx hDC, rechthoek.x, v, oldpoint: LineTo hDC, rechthoek.x + rechthoek.b, v h! = Rechthoek.x FOR i = 0 TO UBOUND(sp) v = ver% - (sp(i) * rechthoek.h) MoveToEx hDC, INT(h!),ver%, oldpoint LineTo hDC, INT(h!),v h! = h! + horstep! NEXT i ReleaseDC hW,hDC FUNCTION = VARPTR(corval(0)) END FUNCTION CALLBACK FUNCTION cbanawin 'shows nr and freq of corval bin @ mouse.x LOCAL x AS LONG, y AS LONG LOCAL rechthoek AS fourlongs LOCAL hdc AS LONG LOCAL h! LOCAL oldpoint AS pointl SELECT CASE CBMSG CASE %WM_Mousemove x = LOWRD(CBLPARAM) y = HIWRD(CBLPARAM) ' hDC = GetDC(cbhndl) GetClientrect CBHNDL, rechthoek ' MoveToEx hDC, x, y, oldpoint ' Lineto hdc, x, 0 'ok h! = Rechthoek.b / 128 h! = x / h! DIALOG SET TEXT CBHNDL, "Analysis (" + STR$(h!) + " - " + STR$(h!/8) +" )" END SELECT END FUNCTION SUB ShowHistograms(sph() AS SINGLE, aph() AS SINGLE, mph() AS SINGLE) STATIC hw AS LONG ' LOCAL sp() AS SINGLE LOCAL v AS LONG LOCAL i AS LONG LOCAL oldpoint AS POINTL LOCAL h!, horstep! LOCAL hDC AS LONG LOCAL Rechthoek AS FourLongs LOCAL ver% LOCAL hpen AS LONG LOCAL horigpen AS LONG IF ISFALSE hw THEN DIALOG NEW 0, "Histogram",,,500,150 TO hw: DIALOG SHOW MODELESS hw hDC = GetDC(hW) GetClientRect hW, Rechthoek ver% = Rechthoek.h '/2 PatBlt hDC, Rechthoek.x,0,Rechthoek.b,Rechthoek.h,%WHITENESS horstep! = Rechthoek.b / UBOUND(sph) MoveToEx hDC, rechthoek.x,ver%, oldpoint: LineTo hDC, rechthoek.x + rechthoek.b,ver% MoveToEx hDc, rechthoek.x + rechthoek.b /2,rechthoek.y, oldpoint: LineTo hDc, rechthoek.x + rechthoek.b/2, rechthoek.y + rechthoek.h ' FOR i = 0 TO UBOUND(corval): sp(i) = (corval(i) / 150): avg = avg + sp(i): NEXT ' avg = avg / (UBOUND(corval) + 1) hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 120)) horigPen = Selectobject(hDC, hPen) h! = Rechthoek.x FOR i = 0 TO UBOUND(sph) v = ver% - (sph(i) * rechthoek.h) MoveToEx hDC, INT(h!),ver%, oldpoint LineTo hDC, INT(h!),v h! = h! + horstep! NEXT i hPen = CreatePen(%PS_SOLID, 1, RGB(120, 0, 0)) horigPen = Selectobject(hDC, hPen) h! = Rechthoek.x FOR i = 0 TO UBOUND(aph) v = ver% - (aph(i) * rechthoek.h) MoveToEx hDC, INT(h!)+3,ver%, oldpoint LineTo hDC, INT(h!)+3,v h! = h! + horstep! NEXT i hPen = CreatePen(%PS_SOLID, 1, RGB(0, 120, 0)) hPen = Selectobject(hDC, hPen) Deleteobject hPen h! = Rechthoek.x FOR i = 0 TO UBOUND(mph) v = ver% - (mph(i) * rechthoek.h) MoveToEx hDC, INT(h!)+6,ver%, oldpoint LineTo hDC, INT(h!)+6,v h! = h! + horstep! NEXT i hPen = Selectobject hDC, horigpen DeleteObject hPen ReleaseDC hW,hDC END SUB FUNCTION summary(pA AS LONG PTR, OPT BYVAL rawflag AS LONG) AS STRING 'creates text summary, if rawflag set only str$ of bin of (lowest freq peek), (lowest freq peek above average), (max peek freq) LOCAL i AS LONG LOCAL b$ DIM corval(128) AS INTEGER AT pA DIM tag(128) AS LONG LOCAL avg AS SINGLE corval(0)=0: corval(1) = 0 'first we try to find lowest peek FOR i = 2 TO 128 IF corval(i) > corval(i+1) THEN EXIT FOR NEXT ' msgbox str$(corval(2)) "corval(2)" b$ = IIF$(rawflag, TRIM$(STR$(i)) +",", "Slowest peek:" + STR$(i) + " (" + STR$(i/16)+") - value:" + STR$(corval(i)) + $CRLF) FOR i = 0 TO 128: tag(i) = i: avg = avg + corval(i): NEXT avg = avg / 127 FOR i = 2 TO 128 IF ABS(corval(i)) < avg THEN ITERATE FOR IF corval(i) > corval(i+1) THEN EXIT FOR NEXT b$ = b$ + IIF$(rawflag, STR$(i) + ",", "Slowest peek for value > average (" + STR$(INT(avg))+ " ):"+STR$(i)+ " (" + STR$(i/16)+ ") - value:" + STR$(corval(i)) + $CRLF) ARRAY SORT corval(), TAGARRAY tag(), DESCEND b$ = b$ + IIF$(rawflag, STR$(tag(0)), "Max peek:" + STR$(tag(0)) + " (" + STR$(tag(0)/16)+ " - value:" + STR$(corval(0)) + $CRLF) FUNCTION = b$ END FUNCTION FUNCTION Parsemov_FileOpenName(title$) AS STRING 'basically calls winapi getopenfilename 'flag: bit 0 set: open html in stead of default file types.. ' 1 : all files by default LOCAL ofn AS OPENFILENAME LOCAL shortfiln AS STRING * 28 LOCAL filnnopath AS STRING * 80 LOCAL filn AS STRING * 300 LOCAL titl AS STRING * 80 LOCAL filtr AS STRING * 200 LOCAL exts AS STRING * 3 LOCAL inidir AS STRING * 256 ofn.lStructSize = SIZEOF(ofn) ofn.hwndOwner = 0 ' ofn.hInstance = myhInst MID$(filn,1) = CHR$(0) ofn.lpStrFile = VARPTR(filn) ofn.nMaxfile = 300 inidir = "C:\b\pb\gmt" CHDIR "C:\b\pb\gmt\" ofn.lpStrInitialDir = VARPTR(inidir) filtr = ".dat" + CHR$(0) + "*.dat" + CHR$(0) +"all files" + CHR$(0) + "*.*" + CHR$(0,0,0,0) ofn.lpStrFilter = VARPTR(filtr) ofn.nFilterIndex=1 titl = title$ ofn.lpStrTitle= VARPTR(titl) ofn.flags = %OFN_FILEMUSTEXIST OR %OFN_LONGNAMES OR %OFN_HIDEREADONLY OR %OFN_NOCHANGEDIR GetOpenFileName ofn FUNCTION = ofn.@lpStrFile END FUNCTION