diff options
-rw-r--r-- | analyzer.lpi | 6 | ||||
-rw-r--r-- | analyzer.lpr | 252 | ||||
-rw-r--r-- | analyzer.lps | 152 | ||||
-rw-r--r-- | valuesunit.pas | 236 |
4 files changed, 570 insertions, 76 deletions
diff --git a/analyzer.lpi b/analyzer.lpi index f5eae51..4961aad 100644 --- a/analyzer.lpi +++ b/analyzer.lpi @@ -29,11 +29,15 @@ <FormatVersion Value="1"/> </local> </RunParams> - <Units Count="1"> + <Units Count="2"> <Unit0> <Filename Value="analyzer.lpr"/> <IsPartOfProject Value="True"/> </Unit0> + <Unit1> + <Filename Value="valuesunit.pas"/> + <IsPartOfProject Value="True"/> + </Unit1> </Units> </ProjectOptions> <CompilerOptions> diff --git a/analyzer.lpr b/analyzer.lpr index 348518c..5b6d150 100644 --- a/analyzer.lpr +++ b/analyzer.lpr @@ -6,22 +6,33 @@ uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} - classes, sysUtils, custApp - { you can add units after this }; + classes, sysUtils, custApp, valuesunit, lowlevelunit, dateutils; type - tOption = (oHelp, oInput); + tOption = (oHelp, oAcceleration, oPosition, oOutput); + + tOptionMeta = record + long: string; + short: char; + value: byte; // bit 0: kann Wert haben; bit 1: muss Wert haben + end; + + tOptionContent = record + typ: tOption; + hasValue: boolean; + value: string; + end; { TAnalyzer } tAnalyzer = class(tCustomApplication) protected - options: array of tOption; - oValues: array of string; + options: array of tOptionContent; + oCnt: array[tOption] of longint; procedure doRun; override; procedure parseOptions; - procedure illegalOption(i: longint); + function optionArgument(o: tOption; i: longint): string; public constructor create(theOwner: tComponent); override; destructor destroy; override; @@ -29,46 +40,70 @@ type end; const - shortOptions: array[tOption] of char = - ('h','i'); - longOptions: array[tOption] of string = ( - 'help', - 'input' - ); - optionValue: array[tOption] of byte = ( // bit 0: kann Wert haben; bit 1: muss Wert haben - 0, - 3 + optionsMeta: array[tOption] of tOptionMeta = ( + (long: 'help'; short: 'h'; value: $00), + (long: 'acceleration'; short: 'a'; value: $03), + (long: 'position'; short: 'p'; value: $03), + (long: 'output'; short: 'o'; value: $03) ); { TAnalyzer } procedure tAnalyzer.doRun; var - errorMsg: string; + i: longint; + values: array of array[boolean] of tValues; begin - // quick check parameters - errorMsg:=checkOptions('hi:', 'help input:'); - if errorMsg<>'' then begin - showException(Exception.Create(ErrorMsg)); + if oCnt[oHelp]>0 then begin + writeHelp; + terminate; + exit; + end; + + if oCnt[oAcceleration]=0 then begin + writeln('error: required option ''-a'' not given'); + writeHelp; halt(1); end; - // parse parameters - if hasOption('h', 'help') then begin + if oCnt[oAcceleration]<>oCnt[oPosition] then begin + writeln('error: option ''-p'' not given equally often as option ''-a'''); writeHelp; - terminate; - exit; + halt(1); end; - if not hasOption('i','input') then begin - writeln('error: required option ''-i'' not given'); + if oCnt[oOutput]<>1 then begin + writeln('error: required option ''-o'' not given exactly once'); writeHelp; halt(1); end; + for i:=0 to oCnt[oAcceleration]-1 do begin + if not fileexists(optionArgument(oAcceleration,i)) then begin + writeln('Acceleration file '''+optionArgument(oAcceleration,i)+''' does not exist'); + halt(1); + end; + if not fileexists(optionArgument(oPosition,i)) then begin + writeln('Position file '''+optionArgument(oPosition,i)+''' does not exist'); + halt(1); + end; + end; + { add your program here } + setlength(values,oCnt[oAcceleration]); + for i:=0 to length(values)-1 do begin + values[i,false]:=tValues.create; + values[i,false].readFromFile(optionArgument(oAcceleration,i)); + values[i,true]:=tValues.create; + values[i,true].readFromFile(values[i,false].timeInterval,optionArgument(oPosition,i)); + values[i,false].intersect(values[i,true]); + end; - writeln(''''+getOptionValue('i','input')+''''); + for i:=0 to length(values)-1 do begin + values[i,false].free; + values[i,true].free; + end; + setlength(values,0); // stop program loop terminate; @@ -76,50 +111,144 @@ end; procedure tAnalyzer.parseOptions; var - i: longint; - s,t: string; - o: tOption; - v,gefunden: boolean; + i,j,incre,jncre: longint; + s: string; + oTypen: array of byte; // 0 = argument; 1 = short option(s); 2 = long option + o: tOption; begin + for o:=low(tOption) to high(tOption) do + oCnt[o]:=0; + + setlength(oTypen,paramcount); + for i:=0 to length(oTypen)-1 do + if copy(paramstr(i+1),1,2)='--' then + oTypen[i]:=2 + else if copy(paramstr(i+1),1,1)='-' then + oTypen[i]:=1 + else + oTypen[i]:=0; + setlength(options,0); - setlength(oValues,0); - i:=1; - while i<=paramcount do begin - s:=paramstr(i); - if copy(s,1,1)<>'-' then - illegalOption(i); - delete(s,1,1); - gefunden:=false; - if copy(s,1,1)='-' then begin // long option - delete(s,1,1); - if pos('=',s)>0 then begin - t:=copy(s,pos('=',s)+1,length(s)); - s:=copy(s,1,pos('=',s)-1); - v:=true; - end - else begin - t:=''; - v:=false; + i:=0; + while i<length(oTypen) do begin + incre:=1; + s:=paramstr(i+1); + case oTypen[i] of + 0: begin + writeln('Spurious argument '''+s+''' at position '+inttostr(i+1)); + halt(1); + end; + 1: begin + delete(s,1,1); + + j:=1; + while j<=length(s) do begin + jncre:=1; + setlength(options,length(options)+1); + options[length(options)-1].hasValue:=false; + options[length(options)-1].value:=''; + options[length(options)-1].typ:=low(optionsMeta); + while options[length(options)-1].typ<high(optionsMeta) do begin + if (optionsMeta[options[length(options)-1].typ].short=s[j]) then + break; + inc(options[length(options)-1].typ); + end; + if (optionsMeta[options[length(options)-1].typ].short<>s[j]) then begin + writeln('Illegal option at position '+inttostr(i+1)+', character '+inttostr(j)+': '''+s[j]+''''); + writeHelp; + halt(1); + end; + + if odd(optionsMeta[options[length(options)-1].typ].value) // may have value + and (((i<length(oTypen)-1) and (oTypen[i+1]=0)) or (j<length(s))) then begin // still arguments left + options[length(options)-1].hasValue:=true; + if j<length(s) then begin + options[length(options)-1].value:=copy(s,j+1,length(s)); + jncre:=length(s); + end + else begin + options[length(options)-1].value:=paramstr(i+2); + inc(incre); + end; + end + else + options[length(options)-1].hasValue:=false; + + if (not options[length(options)-1].hasValue) and odd(optionsMeta[options[length(options)-1].typ].value shr 1) then begin + writeln('Option '''+optionsMeta[options[length(options)-1].typ].short+''' at position '+inttostr(i+1)+', character '+inttostr(j)+' requires an argument!'); + writeHelp; + halt(1); + end; + inc(oCnt[options[length(options)-1].typ]); + inc(j,jncre); + end; end; - for o:=low(longOptions) to high(longOptions) do - if (longOptions[o]<>'') and (longOptions[o]=s) and ((not v) or odd(optionValue[o])) then begin - gefunden:=true; - break; + 2: begin + delete(s,1,2); + + setlength(options,length(options)+1); + + if pos('=',s)>0 then begin + options[length(options)-1].value:=copy(s,pos('=',s)+1,length(s)); + s:=copy(s,1,pos('=',s)-1); + options[length(options)-1].hasValue:=true; + end + else begin + options[length(options)-1].value:=''; + options[length(options)-1].hasValue:=false; + end; + options[length(options)-1].typ:=low(optionsMeta); + while options[length(options)-1].typ<high(optionsMeta) do begin + if (optionsMeta[options[length(options)-1].typ].long<>'') and (optionsMeta[options[length(options)-1].typ].long=s) then + break; + inc(options[length(options)-1].typ); + end; + if (optionsMeta[options[length(options)-1].typ].long='') or (optionsMeta[options[length(options)-1].typ].long<>s) then begin + writeln('Illegal option at position '+inttostr(i+1)+': '''+s+''''); + writeHelp; + halt(1); end; - end; - if not gefunden then - illegalOption(i); + if (not options[length(options)-1].hasValue) // no value yet + and odd(optionsMeta[options[length(options)-1].typ].value) // may have value + and (i<length(oTypen)-1) // still arguments left + and (oTypen[i+1]=0) then begin // next argument is no option + options[length(options)-1].hasValue:=true; + options[length(options)-1].value:=paramstr(i+2); + inc(incre); + end; + + if options[length(options)-1].hasValue and not odd(optionsMeta[options[length(options)-1].typ].value) then begin + writeln('Option '''+optionsMeta[options[length(options)-1].typ].long+''' at position '+inttostr(i+1)+' does not take an argument!'); + writeHelp; + halt(1); + end; - inc(i); + if (not options[length(options)-1].hasValue) and odd(optionsMeta[options[length(options)-1].typ].value shr 1) then begin + writeln('Option '''+optionsMeta[options[length(options)-1].typ].long+''' at position '+inttostr(i+1)+' requires an argument!'); + writeHelp; + halt(1); + end; + inc(oCnt[options[length(options)-1].typ]); + end; + end{of case}; + inc(i,incre); end; end; -procedure tAnalyzer.illegalOption(i: longint); +function tAnalyzer.optionArgument(o: tOption; i: longint): string; +var + j: longint; begin - writeln('Illegal option at position '+inttostr(i)+': '''+paramstr(i)+''''); - writeHelp; - halt(1); + for j:=0 to length(options)-1 do + if (options[j].typ=o) and options[j].hasValue then begin + dec(i); + if i<0 then begin + result:=options[j].value; + exit; + end; + end; + raise exception.create('Option '''+optionsMeta[o].long+''' not available '+inttostr(i+1)+' times'); end; constructor tAnalyzer.create(theOwner: tComponent); @@ -138,6 +267,7 @@ procedure tAnalyzer.writeHelp; begin { add your help code here } writeln('usage: ', exeName, ' -h'); + writeln('usage: ', exeName, ' -i input1 -i input2 -o output'); end; var diff --git a/analyzer.lps b/analyzer.lps index 2f4a0df..e39f80b 100644 --- a/analyzer.lps +++ b/analyzer.lps @@ -3,14 +3,13 @@ <ProjectSession> <Version Value="9"/> <BuildModes Active="Default"/> - <Units Count="2"> + <Units Count="5"> <Unit0> <Filename Value="analyzer.lpr"/> <IsPartOfProject Value="True"/> - <IsVisibleTab Value="True"/> - <TopLine Value="23"/> - <CursorPos Y="52"/> - <UsageCount Value="20"/> + <TopLine Value="75"/> + <CursorPos X="47" Y="99"/> + <UsageCount Value="30"/> <Loaded Value="True"/> </Unit0> <Unit1> @@ -19,26 +18,151 @@ <EditorIndex Value="-1"/> <TopLine Value="348"/> <CursorPos X="44" Y="351"/> - <UsageCount Value="10"/> + <UsageCount Value="9"/> </Unit1> + <Unit2> + <Filename Value="optionunit.pas"/> + <EditorIndex Value="-1"/> + <UsageCount Value="19"/> + </Unit2> + <Unit3> + <Filename Value="valuesunit.pas"/> + <IsPartOfProject Value="True"/> + <IsVisibleTab Value="True"/> + <EditorIndex Value="1"/> + <TopLine Value="151"/> + <CursorPos X="6" Y="180"/> + <UsageCount Value="28"/> + <Loaded Value="True"/> + </Unit3> + <Unit4> + <Filename Value="../units/mystringlistunit.pas"/> + <EditorIndex Value="-1"/> + <TopLine Value="35"/> + <CursorPos X="7" Y="52"/> + <UsageCount Value="9"/> + </Unit4> </Units> - <JumpHistory Count="4" HistoryIndex="3"> + <JumpHistory Count="30" HistoryIndex="29"> <Position1> - <Filename Value="analyzer.lpr"/> - <Caret Line="51" Column="50" TopLine="29"/> + <Filename Value="valuesunit.pas"/> + <Caret Line="8" Column="34" TopLine="2"/> </Position1> <Position2> - <Filename Value="analyzer.lpr"/> - <Caret Line="52" Column="15" TopLine="30"/> + <Filename Value="valuesunit.pas"/> + <Caret Line="92" TopLine="68"/> </Position2> <Position3> <Filename Value="analyzer.lpr"/> - <Caret Line="53" Column="39" TopLine="30"/> + <Caret Line="99" Column="61" TopLine="78"/> </Position3> <Position4> - <Filename Value="analyzer.lpr"/> - <Caret Line="52" Column="38" TopLine="30"/> + <Filename Value="valuesunit.pas"/> + <Caret Line="91" Column="52" TopLine="73"/> </Position4> + <Position5> + <Filename Value="valuesunit.pas"/> + <Caret Line="22" Column="28"/> + </Position5> + <Position6> + <Filename Value="valuesunit.pas"/> + <Caret Line="23" Column="36"/> + </Position6> + <Position7> + <Filename Value="valuesunit.pas"/> + <Caret Line="94" Column="40" TopLine="70"/> + </Position7> + <Position8> + <Filename Value="valuesunit.pas"/> + <Caret Line="102" TopLine="72"/> + </Position8> + <Position9> + <Filename Value="valuesunit.pas"/> + <Caret Line="24" Column="72"/> + </Position9> + <Position10> + <Filename Value="valuesunit.pas"/> + <Caret Line="95" Column="34" TopLine="73"/> + </Position10> + <Position11> + <Filename Value="analyzer.lpr"/> + <Caret Line="98" Column="40" TopLine="77"/> + </Position11> + <Position12> + <Filename Value="valuesunit.pas"/> + <Caret Line="12" Column="16" TopLine="52"/> + </Position12> + <Position13> + <Filename Value="valuesunit.pas"/> + <Caret Line="57" Column="15" TopLine="49"/> + </Position13> + <Position14> + <Filename Value="valuesunit.pas"/> + <Caret Line="58" Column="15" TopLine="49"/> + </Position14> + <Position15> + <Filename Value="valuesunit.pas"/> + <Caret Line="59" Column="15" TopLine="49"/> + </Position15> + <Position16> + <Filename Value="valuesunit.pas"/> + <Caret Line="62" Column="15" TopLine="49"/> + </Position16> + <Position17> + <Filename Value="valuesunit.pas"/> + <Caret Line="63" Column="15" TopLine="49"/> + </Position17> + <Position18> + <Filename Value="valuesunit.pas"/> + <Caret Line="64" Column="15" TopLine="49"/> + </Position18> + <Position19> + <Filename Value="valuesunit.pas"/> + <Caret Line="110" Column="21" TopLine="95"/> + </Position19> + <Position20> + <Filename Value="valuesunit.pas"/> + </Position20> + <Position21> + <Filename Value="valuesunit.pas"/> + <Caret Line="25" Column="26"/> + </Position21> + <Position22> + <Filename Value="valuesunit.pas"/> + <Caret Line="69" Column="21" TopLine="45"/> + </Position22> + <Position23> + <Filename Value="valuesunit.pas"/> + <Caret Line="212" Column="30" TopLine="176"/> + </Position23> + <Position24> + <Filename Value="valuesunit.pas"/> + <Caret Line="38"/> + </Position24> + <Position25> + <Filename Value="analyzer.lpr"/> + <Caret Line="58" Column="3" TopLine="46"/> + </Position25> + <Position26> + <Filename Value="valuesunit.pas"/> + <Caret Line="226" Column="67" TopLine="187"/> + </Position26> + <Position27> + <Filename Value="valuesunit.pas"/> + <Caret Line="47" Column="30" TopLine="22"/> + </Position27> + <Position28> + <Filename Value="analyzer.lpr"/> + <Caret Line="47" Column="30" TopLine="33"/> + </Position28> + <Position29> + <Filename Value="analyzer.lpr"/> + <Caret Line="97" Column="98" TopLine="66"/> + </Position29> + <Position30> + <Filename Value="analyzer.lpr"/> + <Caret Line="9" Column="66" TopLine="3"/> + </Position30> </JumpHistory> </ProjectSession> </CONFIG> diff --git a/valuesunit.pas b/valuesunit.pas new file mode 100644 index 0000000..97d960f --- /dev/null +++ b/valuesunit.pas @@ -0,0 +1,236 @@ +unit valuesunit; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, lowlevelunit; + +type + tWert = record + time: int64; // unix timestamp * 1000 + vec: array['x'..'z'] of extended; + end; + + tValues = class + private + werte: array of tWert; + public + constructor create; + destructor destroy; override; + procedure readFromFile(dat: string); overload; + procedure readFromFile(ti: tExtPoint; dat: string); overload; + procedure readFromFile(minT,maxT: extended; dat: string); overload; + function count: int64; + function timeInterval: tExtPoint; + procedure intersect(v: tValues); + end; + +function interpolate(w1,w2: tWert; x: extended): tWert; inline; +function isoStrToDateTime(s: string): extended; inline; +function timeToInt64(s: string): int64; + +implementation + +uses + math, dateutils; + +constructor tValues.create; +begin + inherited create; + setlength(werte,0); +end; + +destructor tValues.destroy; +begin + setlength(werte,0); + inherited destroy; +end; + +procedure tValues.readFromFile(dat: string); +begin + readFromFile(-infinity,infinity,dat); +end; + +procedure tValues.readFromFile(ti: tExtPoint; dat: string); +begin + readFromFile(ti['x'],ti['y'],dat); +end; + +procedure tValues.readFromFile(minT,maxT: extended; dat: string); +var + f: textfile; + s: string; + i,delta,cnt: int64; + ws: tWert; + c: char; + isXml: boolean; +begin + setlength(werte,0); + fillchar(ws,sizeOf(tWert),0); + cnt:=0; + assignFile(f,dat); + reset(f); + isXml:=false; + s:=''; + while (s<>'') or not eof(f) do begin + if s='' then + readln(f,s); + if s='' then + continue; + + if (not isXml) and (not (s[1] in ['0'..'9'])) then begin + if copy(s,1,6)='<?xml ' then + isXml:=true + else begin + s:=''; + continue; + end; + end; + + if isXml then begin + if pos('<trkpt lat="',s)=0 then begin + s:=''; + continue; + end; + + delete(s,1,pos('<trkpt lat="',s)); + delete(s,1,pos('"',s)); + ws.vec['y']:=strtofloat(erstesArgument(s,'" ')); + if pos('lon="',s)<>1 then + raise exception.create('Syntax error in '''+dat+''' (lon) - no valid gpx file.'#10'Hickup: '''+copy(s,1,40)+' ...'''); + delete(s,1,pos('"',s)); + ws.vec['x']:=strtofloat(erstesArgument(s,'"')); + if pos('><ele>',s)<>1 then + raise exception.create('Syntax error in '''+dat+''' (lat) - no valid gpx file.'#10'Hickup: '''+copy(s,1,40)+' ...'''); + delete(s,1,pos('>',s)); + delete(s,1,pos('>',s)); + ws.vec['z']:=strtofloat(erstesArgument(s,'<')); + if pos('/ele><time>',s)<>1 then + raise exception.create('Syntax error in '''+dat+''' (ele) - no valid gpx file.'#10'Hickup: '''+copy(s,1,40)+' ...'''); + delete(s,1,pos('>',s)); + delete(s,1,pos('>',s)); + ws.time:=timeToInt64(erstesArgument(s,'<')); + if pos('/time>',s)<>1 then + raise exception.create('Syntax error in '''+dat+''' (time) - no valid gpx file.'#10'Hickup: '''+copy(s,1,40)+' ...'''); + end + else begin + ws.time:=strtoint64(erstesArgument(s,';')); + erstesArgument(s,';'); + for c:='x' to 'z' do + ws.vec[c]:=strtofloat(erstesArgument(s,';')); + s:=''; + end; + + if cnt>0 then + delta:=ws.time-werte[cnt-1].time + else + delta:=1; + + cnt:=cnt+delta; + if cnt>=length(werte) then + setlength(werte,cnt+64*1024); + if cnt>=length(werte) then + raise exception.create('Failed to enlarge array.'); + + if delta=1 then + move(ws,werte[cnt-1],sizeOf(tWert)) + else + for i:=delta-1 downto 0 do + werte[cnt-i-1]:= + interpolate( + werte[cnt-delta-1], + ws, + (delta-i)/delta + ); + + if werte[0].time<minT then begin + delta:=cnt-1; + while (werte[delta].time>=minT) and (delta>0) do + dec(delta); + for i:=delta to cnt-1 do + move(werte[i],werte[i-delta],sizeOf(tWert)); + cnt:=cnt-delta; + end; + + if werte[cnt-1].time>maxT then begin + while (cnt>0) and (werte[cnt-2].time>maxT) do + dec(cnt); + break; + end; + end; + closeFile(f); + setlength(werte,cnt); +end; + +function tValues.count: int64; +begin + result:=length(werte); +end; + +function tValues.timeInterval: tExtPoint; +begin + result['x']:=werte[0].time; + result['y']:=werte[count-1].time; +end; + +procedure tValues.intersect(v: tValues); +begin + if +end; + +// general functions *********************************************************** + +function interpolate(w1,w2: tWert; x: extended): tWert; inline; +var + c: char; +begin + result.time:= + round(w1.time*(1-x) + w2.time*x); + for c:='x' to 'z' do begin + result.vec[c]:= + round(w1.vec[c]*(1-x) + w2.vec[c]*x); + end; +end; + +function isoStrToDateTime(s: string): extended; inline; +begin + if (length(s)<>19) or + (s[5]<>'-') or + (s[8]<>'-') or + (s[11]<>' ') or + (s[14]<>':') or + (s[17]<>':') then + raise exception.create(''''+s+''' is not a valid iso date-time'); + result:= + encodeDate( + strToInt(copy(s,1,4)), + strToInt(copy(s,6,2)), + strToInt(copy(s,9,2)) + ) + + encodeTime( + strToInt(copy(s,12,2)), + strToInt(copy(s,15,2)), + strToInt(copy(s,18,2)), + 0 + ); +end; + +function timeToInt64(s: string): int64; +begin + if (length(s)<>20) or + (s[5]<>'-') or + (s[8]<>'-') or + (s[11]<>'T') or + (s[14]<>':') or + (s[17]<>':') or + (s[20]<>'Z') then + raise exception.create(''''+s+''' is not a valid date-time'); + delete(s,20,1); + s[11]:=' '; + result:=round(dateTimeToUnix(isoStrToDateTime(s))*1000); +end; + +end. + |