Commit cf7f7089a1b1267b828b64200e5b11b60cfc6710

Authored by caveenj
1 parent ba2cabcb
Exists in master and in 1 other branch odf2csvP3

Premiere version fonctionnelle de odf2csv

Version fonctionnelle sous linux avec
elimination des donnees douteuses et erronnees
et moyennage a intervalles reguliers
src/class_variable.py 0 → 100644
... ... @@ -0,0 +1,78 @@
  1 +#########################################################
  2 +# Classe variable pour les fichiers ODF
  3 +#
  4 +# Pour chaque variable du fichier, on enmagasine
  5 +# le nom, le code, les unites, le Print_field_width, le print_decimal_places
  6 +# et on attribue un numero a la variable.
  7 +# Ce numero permet de trouver l'objet variable contenant
  8 +# les parametres de controle de qualite d'une variable donnee:
  9 +# Par exemple, si la variable temperature a le numero 1, ses QQQQ ont
  10 +# le numero 2
  11 +#
  12 +#James Caveen : juillet 2006
  13 +#
  14 +#Parametres d'instanciation:
  15 +# num_var: numero sequentiel de la variable
  16 +# nom: Nom complet (e.g., 'Sensor Depth Below...)
  17 +# code: Nom court (e.g., DEPH_01)
  18 +# unites: unites dela variable
  19 +# p_f_w: Print field width (utilise par getvaleur)
  20 +# p_d_p: Print decimal places (utilise par getvaleur)
  21 +################################################################
  22 +
  23 +
  24 +
  25 +class variable:
  26 + def __init__(self,num_var,nom,code,unites,p_f_w,p_d_p):
  27 + self.numero = num_var
  28 + self.nom = nom
  29 + self.code = code
  30 + self.unites = unites
  31 + self.p_f_w = p_f_w
  32 + self.p_d_p = p_d_p
  33 + self.valeurs = []
  34 +
  35 +
  36 + def __str__(self):
  37 + return "Numero:%d Nom: %s Code: %s Unites: %s PRINT_FIELD_WIDTH: %s PRINT_DECIMAL_PLACES: %s" % \
  38 + (self.numero,self.nom,self.code,self.unites,self.p_f_w,self.p_d_p)
  39 +
  40 +
  41 + def getnumero(self):
  42 + return self.numero
  43 + def getnom (self):
  44 + return self.nom
  45 + def getcode (self):
  46 + return self.code
  47 + def getunites (self):
  48 + return self.unites
  49 + def getp_f_w (self):
  50 + return self.p_f_w
  51 + def getp_d_p (self):
  52 + return self.p_d_p
  53 +
  54 + def getnbval(self):
  55 + return len(self.valeurs)
  56 +
  57 + def addvaleur(self,valeur):
  58 + self.valeurs.append(valeur)
  59 +
  60 + def setvaleur(self,index, valeur):
  61 + self.valeurs[index] = valeur
  62 +
  63 + def replaceallvaleurs(self, valeurs):
  64 + self.valeurs[:] = valeurs
  65 +
  66 + def getvaleur(self,index):
  67 + format1 = '%'
  68 + format2 = '%d.%df' % (self.p_f_w,self.p_d_p)
  69 + format = format1 + format2
  70 + return format % float(self.valeurs[index])
  71 +
  72 +
  73 + def getallvaleurs(self):
  74 + for val in self.valeurs:
  75 + print val
  76 +
  77 +
  78 +
... ...
src/odf2csv.py 0 → 100755
... ... @@ -0,0 +1,497 @@
  1 +#!/usr/bin/env python
  2 +
  3 +#####################################
  4 +#
  5 +#Conversion de fichiers ODF a csv
  6 +#
  7 +#Auteur: James Caveen
  8 +# Ecrit a partir du programme fortran
  9 +# odf2txt de C. Lafleur (IML)
  10 +#
  11 +####################################
  12 +
  13 +import sys
  14 +import getopt
  15 +import re
  16 +import math
  17 +from class_variable import variable
  18 +import odf_fcns
  19 +
  20 +RemplacerDouteux = True
  21 +RemplacerErreur = True
  22 +CalculerMoyenne = True
  23 +intervalle = 1.0
  24 +#On recupere les parametres d'appel
  25 +
  26 +try:
  27 + opts,args = getopt.getopt(sys.argv[1:], \
  28 + 'i:o:m:deah',\
  29 + ['input=','output=',\
  30 + 'mean=','all','error',\
  31 + 'douteux','help'])
  32 +except getopt.error, msg:
  33 + print msg
  34 + odf_fcns.usage()
  35 + sys.exit(2)
  36 +#Processing des parametres
  37 +
  38 +for o , a in opts:
  39 + if o in ("-h","--help"):
  40 + odf_fcns.usage()
  41 + sys.exit(2)
  42 +
  43 + elif o in ("-i","--input"):
  44 + if a in ("-i","--input","-o","--output","-d",\
  45 + "--douteux","-e","--error","-h","--help","--mean","-m",\
  46 + "-a","--all"):
  47 + odf_fcns.usage()
  48 + sys.exit(2)
  49 +
  50 + fnomin = a
  51 + elif o in ("-o","--output"):
  52 + if a in ("-i","--input","-o","--output","-d",\
  53 + "--douteux","-e","--error","-h","--help","--mean","-m",\
  54 + "-a","--all"):
  55 + odf_fcns.usage()
  56 + sys.exit(2)
  57 +
  58 + fnomout = a
  59 + elif o in ("-m","--mean"):
  60 + if a in ("-i","--input","-o","--output","-d",\
  61 + "--douteux","-e","--error","-h","--help","--mean","-m",\
  62 + "-a","--all"):
  63 + odf_fcns.usage()
  64 + sys.exit(2)
  65 +
  66 + intervalle = float(a)
  67 +
  68 + elif o in ("-d","--douteux"):
  69 + RemplacerDouteux = False
  70 + elif o in ("-e","--error"):
  71 + RemplacerErreur = False
  72 + elif o in ("-a","--all"):
  73 + CalculerMoyenne = False
  74 + else:
  75 + print "Option inconnue"
  76 + odf_fcns.usage()
  77 + sys.exit(2)
  78 +
  79 +
  80 +
  81 +#Compilation des expressions regulieres a chercher
  82 +# dans le fichier ODF
  83 +PAR_HEAD = re.compile(r'PARAMETER_HEADER')
  84 +DATA = re.compile(r'-- DATA --')
  85 +NAME= re.compile(r'NAME')
  86 +UNITS = re.compile(r'UNITS')
  87 +CODE = re.compile(r'CODE')
  88 +P_F_W = re.compile(r'PRINT_FIELD_WIDTH')
  89 +P_D_P = re.compile(r'PRINT_DECIMAL_PLACES')
  90 +FILE_SPEC = re.compile(r'FILE_SPECIFICATION')
  91 +CRUISE_NUMBER = re.compile(r'CRUISE_NUMBER')
  92 +START_DATE = re.compile(r'START_DATE_TIME')
  93 +INIT_LAT = re.compile(r'INITIAL_LATITUDE')
  94 +INIT_LON = re.compile(r'INITIAL_LONGITUDE')
  95 +COMMENT = re.compile(r'EVENT_COMMENTS')
  96 +QCOMMENT = re.compile(r'QUALITY_COMMENTS')
  97 +QQQQ = re.compile(r'QQQQ')
  98 +QCFF = re.compile(r'QCFF')
  99 +DEPTH = re.compile(r'DEPH_01')
  100 +PRES = re.compile(r'PRES_01')
  101 +
  102 +
  103 +
  104 +#Initialiser les listes a des listes vides
  105 +varlist=[] # Liste des objets de type variable
  106 +comment = [] #Liste des commentaires d'evenements
  107 +qcomment =[] #Liste des commentaires de qualite
  108 +depth = [] #Lise des profondeurs cibles pour moyennes
  109 +
  110 +#Initialiser un dictionnaire permettant d'associer
  111 +#une variable et son champ QQQQ
  112 +
  113 +var_q = {}
  114 +
  115 +# Ouverture du fichier ODF a traiter
  116 +
  117 +fin = open(fnomin,mode='r')
  118 +
  119 +#Ouverture du fichier csv de sortie en mode ajout
  120 +
  121 +fout = open(fnomout,mode='a')
  122 +
  123 +num_var = 0
  124 +num_par = 0
  125 +par_trouve = False
  126 +
  127 +#Recherche de tous les parametres contenus
  128 +#dans le fichier ODF
  129 +#Pour chaque parametre trouve, on cree un objet
  130 +#de classe variable et on y conserve l'information
  131 +#qui lui est propre
  132 +for ligne in fin:
  133 + if (not PAR_HEAD.search(ligne)):
  134 + pass
  135 + else:
  136 + par_trouve = True
  137 +
  138 + if (par_trouve):
  139 + if(NAME.search(ligne)):
  140 + num_par = num_par + 1
  141 + nom = ligne.split('=')[1]
  142 + nom = nom.split(',')[0]
  143 + elif (UNITS.search(ligne)):
  144 + num_par = num_par + 1
  145 + unites = ligne.split('=')[1]
  146 + unites = unites.split(',')[0]
  147 + elif (CODE.search(ligne)):
  148 + num_par = num_par + 1
  149 + code = ligne.split('=')[1]
  150 + code = code.split(',')[0]
  151 + elif (P_F_W.search(ligne)):
  152 + num_par = num_par + 1
  153 + p_f_w = ligne.split('=')[1]
  154 + p_f_w = p_f_w.split(',')[0]
  155 + elif (P_D_P.search(ligne)):
  156 + num_par = num_par + 1
  157 + p_d_p = ligne.split('=')[1]
  158 + p_d_p = p_d_p.split(',')[0]
  159 +
  160 + if (num_par == 5):
  161 + num_par = 0
  162 + par_trouve = False
  163 + v = variable(num_var,nom.strip(),code.strip(),unites.strip(),int(p_f_w),int(p_d_p))
  164 + varlist.append(v)
  165 +
  166 + #Si la variable est de type marqueur de qualite
  167 + #On ajoute une entree au dictionnaire
  168 + if(QQQQ.search(code)):
  169 + var_q[varlist[num_var-1]] = v
  170 +
  171 + num_var = num_var + 1
  172 +
  173 +
  174 +#
  175 +#########################################
  176 +# Lecture des information sur la mission#
  177 +#########################################
  178 +#
  179 +
  180 +fin.seek(0,0)
  181 +for ligne in fin:
  182 +
  183 +
  184 + if (DATA.search(ligne)):
  185 + break
  186 +
  187 + elif(FILE_SPEC.search(ligne)):
  188 + nom = ligne.split('=')[1]
  189 + file_spec = nom.split(',')[0]
  190 +
  191 + elif (CRUISE_NUMBER.search(ligne)):
  192 + nom = ligne.split('=')[1]
  193 + cruise_number = nom.split(',')[0]
  194 +
  195 + elif (START_DATE.search(ligne)):
  196 + nom = ligne.split('=')[1]
  197 + start_date = nom.split(',')[0]
  198 +
  199 + elif (INIT_LAT.search(ligne)):
  200 + nom = ligne.split('=')[1]
  201 + init_lat = nom.split(',')[0]
  202 +
  203 + elif (INIT_LON.search(ligne)):
  204 + nom = ligne.split('=')[1]
  205 + init_lon = nom.split(',')[0]
  206 +
  207 + elif (COMMENT.search(ligne)):
  208 + nom = ligne.split('=')[1]
  209 + com = nom.split(',')[0]
  210 + comment.append(com)
  211 +
  212 + elif (QCOMMENT.search(ligne)):
  213 + nom = ligne.split('=')[1]
  214 + com = nom.split(',')[0]
  215 + qcomment.append(com)
  216 +
  217 +
  218 +print "Cruise_Number: ", cruise_number
  219 +print "Original_Filename: ",file_spec
  220 +print "GMT_Time: ", start_date
  221 +print "Latitude: ",init_lat
  222 +print "Longitude: ", init_lon
  223 +
  224 +# for c in comment:
  225 +# print "Event_Comment: ", c
  226 +
  227 +# for c in qcomment:
  228 +# print "Quality_Comment: ", c
  229 +
  230 +
  231 +
  232 +
  233 +# Lecture des valeurs des variables dans le fichier
  234 +
  235 +fin.seek(0,0)
  236 +data_trouve = False
  237 +for ligne in fin:
  238 + if (not data_trouve):
  239 + if (not DATA.search(ligne)):
  240 + pass
  241 + else:
  242 + data_trouve = True
  243 + else:
  244 + ligne = ligne[:-2] # Oter les fins de lignes (fichier DOS, 2 car: \r\n)
  245 + chaine = ligne.split(None,num_var) # Separer la ligne en mots
  246 +
  247 + i = 0
  248 + for var in varlist:
  249 + var.addvaleur(float(chaine[i]))
  250 + i = i + 1
  251 +
  252 +
  253 +
  254 +# On calcule la profondeur si elle est absente du fichier
  255 +# et on l'ajoute a la liste des variables disponibles
  256 +
  257 +
  258 +depth_present = False
  259 +for var in varlist:
  260 + if (DEPTH.search(var.getcode())):
  261 + depth_present = True
  262 + break
  263 +
  264 +if (not depth_present):
  265 + for var in varlist:
  266 + if(not PRES.search(var.getcode())):
  267 + pass
  268 + else:
  269 + v = variable(num_var,\
  270 + 'Sensor depth below sea surface','DEPH_01','meters',\
  271 + 10,3)
  272 + for pres in var.valeurs:
  273 + v.addvaleur( odf_fcns.pres2depth(pres,float(init_lat)))
  274 + varlist.append(v)
  275 + num_var = num_var + 1
  276 + #On copie les marqueurs de qualite de pres a prof
  277 + if( var_q.has_key(var)):
  278 + presq = var_q.get(var)
  279 +
  280 + profq = variable(num_var,'Quality flags: Sensor depth below sea surface','Q','none', 2,0)
  281 + varlist.append(profq)
  282 +
  283 + var_q[varlist[num_var -1]] = profq
  284 +
  285 + for i in range(presq.getnbval()):
  286 + profq.addvaleur( presq.valeurs[i])
  287 +
  288 + num_var = num_var +1
  289 + break
  290 +
  291 +
  292 +########################################################
  293 +# Remplacer les donnees erronnees ou douteuses par -99.0
  294 +# controle par les parametres -d et -e
  295 +########################################################
  296 +if (RemplacerErreur):
  297 + for var in varlist:
  298 + if (var_q.has_key(var)):
  299 + qualite = var_q.get(var)
  300 +
  301 + for i in range(var.getnbval()):
  302 + if (int(qualite.getvaleur(i)) == 4):
  303 + qualite.setvaleur(i,9) #donnee mise a manquant
  304 + var.setvaleur(i,-99.0)
  305 + qcomment.append('Donnees erronnees (Q=4) remplacees par -99.0')
  306 +
  307 +
  308 +if (RemplacerDouteux):
  309 + for var in varlist:
  310 + if (var_q.has_key(var)):
  311 + qualite = var_q.get(var)
  312 +
  313 + for i in range(var.getnbval()):
  314 + if (int(qualite.getvaleur(i)) == 3):
  315 + qualite.setvaleur(i,9) #donnee mise a manquant
  316 + var.setvaleur(i,-99.0)
  317 +
  318 + qcomment.append('Donnees douteuses (Q=3) remplacees par -99.0')
  319 +
  320 +
  321 +#
  322 +####################################################
  323 +# Moyenner les donnees a intervalles reguliers en metres
  324 +# Par defaut on moyenne au metre a moins que l'utilisateur
  325 +# specifie un autre intervalle via la clef -m (--mean) <intervalle>
  326 +#
  327 +# si la clef -a (--all) est utilisee on ne moyenne pas
  328 +#
  329 +# Note: les variables PRES et DEPH sont traitees en dernier
  330 +# car elles sont utilisees pour le calcul des moyennes
  331 +# des autres variables; on ne peut donc pas remplacer
  332 +# leurs valeurs reelles avec celles moyennes avant que
  333 +# toutes les autres variables aient ete traitees
  334 +#
  335 +##############################################################
  336 +#
  337 +
  338 +
  339 +if (CalculerMoyenne):
  340 + chaine = 'Donnees moyennees au(x) ' + str(intervalle) + ' metre(s)'
  341 + qcomment.append(chaine)
  342 +
  343 + for var in varlist:
  344 + if (DEPTH.search(var.getcode())):
  345 + min_depth = math.ceil(min([x for x in var.valeurs if x > -99.0]))
  346 + max_depth = math.floor(max([x for x in var.valeurs if x > -99.0]))
  347 + num_depth = int((max_depth - min_depth)/intervalle) + 1
  348 + profondeur = var
  349 + break
  350 +
  351 + for i in range(num_depth):
  352 + depth.append(min_depth + float((i)*intervalle))
  353 +
  354 +
  355 +# Ici, on traite toutes les variables sauf PRES et DEPTH
  356 + for var in varlist:
  357 + if not DEPTH.search(var.getcode()) and not PRES.search(var.getcode()):
  358 + print "On traite:", var.getcode()
  359 + if(var_q.has_key(var)):
  360 + qualite = var_q.get(var)
  361 + mean_dat = []
  362 + qualite_dat = []
  363 +
  364 + for k in range(num_depth):
  365 + sum_dat=0
  366 + n_dat=0
  367 + for i in range(var.getnbval()):
  368 + if float(profondeur.getvaleur(i)) > (depth[k]-intervalle/2.0) and \
  369 + float(profondeur.getvaleur(i)) <= (depth[k]+intervalle/2.0) and \
  370 + float(var.getvaleur(i)) > -99 :
  371 + sum_dat=sum_dat+ float(var.getvaleur(i))
  372 + n_dat=n_dat+1
  373 +
  374 + if n_dat == 0 :
  375 + mean_dat.append(-99.0)
  376 + else:
  377 + mean_dat.append(sum_dat/n_dat)
  378 +
  379 + if mean_dat[k] == -99:
  380 + qualite_dat.append(9)
  381 + elif mean_dat[k] > -99:
  382 + qualite_dat.append(1)
  383 +
  384 + var.replaceallvaleurs(mean_dat)
  385 + qualite.replaceallvaleurs(qualite_dat)
  386 +
  387 +
  388 +#On traite DEPTH et PRES
  389 + for var in varlist:
  390 + if DEPTH.search(var.getcode()) or PRES.search(var.getcode()):
  391 + print "On traite:", var.getcode()
  392 + if(var_q.has_key(var)):
  393 + qualite = var_q.get(var)
  394 + mean_dat = []
  395 + qualite_dat = []
  396 + for k in range(num_depth):
  397 + sum_dat=0
  398 + n_dat=0
  399 + for i in range(var.getnbval()):
  400 + if DEPTH.search(var.getcode()):
  401 + sum_dat=sum_dat+depth[k]
  402 + n_dat=n_dat+1
  403 + elif PRES.search(var.getcode()):
  404 + sum_dat=sum_dat+odf_fcns.depth2pres(depth[k],float(init_lat))
  405 + n_dat=n_dat+1
  406 +
  407 +
  408 + if n_dat == 0 :
  409 + mean_dat.append(-99.0)
  410 + else:
  411 + mean_dat.append(sum_dat/n_dat)
  412 +
  413 + if mean_dat[k] == -99:
  414 + qualite_dat.append(9)
  415 + elif mean_dat[k] > -99:
  416 + qualite_dat.append(1)
  417 +
  418 + var.replaceallvaleurs(mean_dat)
  419 + qualite.replaceallvaleurs(qualite_dat)
  420 +
  421 +
  422 +
  423 +
  424 +
  425 +#
  426 +####################################################
  427 +#Ecriture sur le fichier de sortie
  428 +####################################################
  429 +#
  430 +
  431 +#Ecriture des parametres de la mission
  432 +
  433 +chaine = "Cruise_Number: "+ cruise_number + "\n"
  434 +fout.write(chaine)
  435 +chaine = "Original_Filename: "+file_spec + "\n"
  436 +fout.write(chaine)
  437 +chaine = "GMT_Time: "+ start_date + "\n"
  438 +fout.write(chaine)
  439 +chaine = "Latitude: "+init_lat + "\n"
  440 +fout.write(chaine)
  441 +chaine = "Longitude: "+ init_lon + "\n"
  442 +fout.write(chaine)
  443 +
  444 +for c in comment:
  445 + chaine = "Event_Comment: "+ c + "\n"
  446 + fout.write(chaine)
  447 +
  448 +for c in qcomment:
  449 + chaine = "Quality_Comment: "+ c + "\n"
  450 + fout.write(chaine)
  451 +
  452 +#Un petit saut de ligne
  453 +fout.write("\n")
  454 +
  455 +#######################################################
  456 +#Ecriture de la description des variables
  457 +#en format csv avec tabulateur
  458 +#Note:Si on a fait une moyenne des donnees, on elimine
  459 +# la variable QCFF
  460 +#######################################################
  461 +
  462 +chaine = ''
  463 +for var in varlist:
  464 + if CalculerMoyenne and QCFF.search(var.getcode()):
  465 + pass
  466 + else:
  467 + chaine = chaine + var.getcode() + '\t'
  468 +
  469 +chaine = chaine +'\n'
  470 +fout.write(chaine)
  471 +
  472 +
  473 +chaine = ''
  474 +for var in varlist:
  475 + if CalculerMoyenne and QCFF.search(var.getcode()):
  476 + pass
  477 + else:
  478 + chaine = chaine + var.getunites() + '\t'
  479 +
  480 +chaine = chaine +'\n'
  481 +fout.write(chaine)
  482 +
  483 +#Ecriture des valeurs des variables dans le fichier
  484 +#en format csv avec tabulateur
  485 +for i in range(varlist[0].getnbval()):
  486 + chaine =''
  487 + for var in varlist:
  488 + if CalculerMoyenne and QCFF.search(var.getcode()):
  489 + pass
  490 + else:
  491 + chaine = chaine + var.getvaleur(i) + '\t'
  492 + chaine = chaine + '\n'
  493 + fout.write(chaine)
  494 +
  495 +
  496 +#Fin de odf2csv.py
  497 +
... ...
src/odf_fcns.py 0 → 100644
... ... @@ -0,0 +1,65 @@
  1 +########################################################
  2 +# Defintion des fonctions utilisees par odf2txt.py
  3 +#
  4 +# James Caveen : juillet 2006
  5 +#
  6 +# Fonctions ecrites a partir du programme odf2txt de C. Lafleur (IML)
  7 +########################################################
  8 +import math
  9 +
  10 +#------------------------------------------------
  11 +# Calcul de la profondeur (metres) a partir de la pression (db)
  12 +#------------------------------------------------
  13 +
  14 +def pres2depth(pin,lat):
  15 + c = [9.72659e0, -2.2512e-5, 2.279e-10, -1.82e-15]
  16 +
  17 + gammap = 2.184e-6
  18 +
  19 + p = pin
  20 +
  21 + slat = math.sin( lat/57.29578 )
  22 + slat2 = slat*slat
  23 +
  24 + g = 9.780318*( 1. + 5.2788e-3*slat2 + 2.36e-5*slat2*slat2)
  25 +
  26 + tmp1 = ( ( ( c[3] *p + c[2] )*p + c[1] )*p + c[0] )*p
  27 +
  28 + tmp2 = g + .5*gammap*p
  29 +
  30 + return tmp1/tmp2
  31 +
  32 +
  33 +#------------------------------------------------
  34 +# Calcul de la pression (db) a partir de la profondeur (metres)
  35 +#------------------------------------------------
  36 +
  37 +
  38 +def depth2pres(din,lat):
  39 +
  40 + c = [5.92e-3, 5.25e-3, 8.84e-6, 4.42e-6]
  41 + slat = math.sin( lat/57.29578 )
  42 + slat2 = slat*slat
  43 + c1 = c[0] + slat2*c[1]
  44 + return ((1-c1)-math.sqrt(((1-c1)**2) - (c[2]*din)))/c[3]
  45 +
  46 +
  47 +
  48 +def usage():
  49 +
  50 + print "Usage: "
  51 + print "odf2csv -i ficin -o ficout [-m interval | -a ] [-d] [-e ]"
  52 + print " ou:"
  53 + print "odf2csv --input ficin --output ficout [--mean interval | --all] [--error] [--doubtfull]"
  54 + print ""
  55 + print "ficin : fichier entree (input file)"
  56 + print "ficout: fichier sortie (output file"
  57 + print "interval : intervale de profondeurs pour donnees moyennees"
  58 + print " (depth interval for mean data)"
  59 + print "-a : toutes les donnees : ne pas moyenner"
  60 + print " (all data : do not average)"
  61 + print "-d : conserver donnees douteuses (keep data flaged as doubtfull)"
  62 + print "-e : conserver donnees erronnees (keep data flaged as erroneous)"
  63 +
  64 +
  65 +
... ...
test_data/BT_1979005_17_1_.ODF 0 → 100644
... ... @@ -0,0 +1,158 @@
  1 +ODF_HEADER,
  2 + FILE_SPECIFICATION = 'BT_1979005_17_1_',
  3 +CRUISE_HEADER,
  4 + COUNTRY_INSTITUTE_CODE = 1830,
  5 + CRUISE_NUMBER = '1979005',
  6 + ORGANIZATION = 'DSO',
  7 + CHIEF_SCIENTIST = 'Jean-Claude Therriault',
  8 + START_DATE = '25-OCT-1979 00:00:00.00',
  9 + END_DATE = '03-NOV-1979 23:59:00.00',
  10 + PLATFORM = 'Unknown',
  11 + CRUISE_NAME = 'Mission 5, Octobre 1979, Variations saisonnieres de l`estuaire maritime',
  12 + CRUISE_DESCRIPTION = 'MEDS cruise number 1979005',
  13 +EVENT_HEADER,
  14 + DATA_TYPE= 'BT',
  15 + EVENT_NUMBER= '17',
  16 + EVENT_QUALIFIER1= '1',
  17 + EVENT_QUALIFIER2= '',
  18 + CREATION_DATE= '27-APR-2005 12:56:02.80',
  19 + ORIG_CREATION_DATE= '17-NOV-1858 00:00:00.00',
  20 + START_DATE_TIME= '30-OCT-1979 18:04:00.00',
  21 + END_DATE_TIME= '17-NOV-1858 00:00:00.00',
  22 + INITIAL_LATITUDE= 48.983300,
  23 + INITIAL_LONGITUDE= -68.316700,
  24 + END_LATITUDE= -99.990000,
  25 + END_LONGITUDE= -999.990000,
  26 + MIN_DEPTH= 0.00,
  27 + MAX_DEPTH= 124.30,
  28 + SAMPLING_INTERVAL= -99.0,
  29 + SOUNDING= 186.00,
  30 + DEPTH_OFF_BOTTOM= 61.70,
  31 + EVENT_COMMENTS= 'MEDS event number 24',
  32 + EVENT_COMMENTS= 'La profondeur du fond est celle à la position visée',
  33 +INSTRUMENT_HEADER,
  34 + INST_TYPE= 'Bathythermograph',
  35 + MODEL= 'Manual',
  36 + SERIAL_NUMBER= '',
  37 + DESCRIPTION= '',
  38 +QUALITY_HEADER,
  39 + QUALITY_DATE= '27-APR-2005 12:55:54.41',
  40 + QUALITY_TESTS= 'Test 1.1: GTSPP Platform Identification',
  41 + QUALITY_TESTS= 'Test 1.2: GTSPP Impossible Date/Time',
  42 + QUALITY_TESTS= 'Test 1.3: GTSPP Impossible Location',
  43 + QUALITY_TESTS= 'Test 1.4: GTSPP Position on Land',
  44 + QUALITY_TESTS= 'Test 1.5: GTSPP Impossible Speed',
  45 + QUALITY_TESTS= 'Test 1.6: GTSPP Impossible Sounding',
  46 + QUALITY_TESTS= 'Test 5.1: GTSPP Cruise Track Visual Inspection',
  47 + QUALITY_TESTS= 'Test 2.0: IML Minimum Descent Rate (2) (0.10m/s)',
  48 + QUALITY_TESTS= 'Test 2.1: GTSPP Global Impossible Parameter Values (4)',
  49 + QUALITY_TESTS= 'Test 2.2: GTSPP Regional Impossible Parameter Values (8)',
  50 + QUALITY_TESTS= 'Test 2.3: GTSPP Increasing Depth (16)',
  51 + QUALITY_TESTS= 'Test 2.4: GTSPP Profile Envelope (Temperature and Salinity) (32)',
  52 + QUALITY_TESTS= 'Test 2.7: GTSPP Spike in Temperature and Salinity (one point) (256)',
  53 + QUALITY_TESTS= 'Test 2.8: GTSPP Top and Bottom Spike in Temperature and Salinity (512)',
  54 + QUALITY_TESTS= 'Test 2.9: GTSPP Gradient in Temperature and Salinity (1024)',
  55 + QUALITY_TESTS= 'Test 3.5: IML Petrie Monthly Climatology (Temperature, Salinity and Sigma-T)',
  56 + QUALITY_TESTS= 'Test 5.2: GTSPP Profile Visual Inspection',
  57 + QUALITY_COMMENTS= 'QCFF values are derived from tests of GTSPP stage 2',
  58 + QUALITY_COMMENTS= 'A quality flag modified by hand has a QCFF value of 1',
  59 +HISTORY_HEADER,
  60 + CREATION_DATE= '13-APR-2005 15:46:44.64',
  61 + PROCESS= 'Conversion à partir d`un fichier MBT en provenance du MEDS',
  62 +PARAMETER_HEADER,
  63 + TYPE= 'DOUB',
  64 + NAME= 'Sensor Depth below Sea Surface',
  65 + UNITS= 'metres',
  66 + CODE= 'DEPH_01',
  67 + NULL_VALUE= -9.900000E+001,
  68 + PRINT_FIELD_WIDTH= 9,
  69 + PRINT_DECIMAL_PLACES= 2,
  70 + ANGLE_OF_SECTION= 0.000000,
  71 + MAGNETIC_VARIATION= 0.000000,
  72 + DEPTH= 0.000000 ,
  73 + MINIMUM_VALUE= 0,
  74 + MAXIMUM_VALUE= 124.3,
  75 + NUMBER_VALID= 15,
  76 + NUMBER_NULL= 0,
  77 +PARAMETER_HEADER,
  78 + TYPE= 'DOUB',
  79 + NAME= 'Quality flag: Sensor Depth below Sea Surface',
  80 + UNITS= 'none',
  81 + CODE= 'QQQQ_01',
  82 + NULL_VALUE= -9.900000E+001,
  83 + PRINT_FIELD_WIDTH= 1,
  84 + PRINT_DECIMAL_PLACES= 0,
  85 + ANGLE_OF_SECTION= 0.000000,
  86 + MAGNETIC_VARIATION= 0.000000,
  87 + DEPTH= 0.000000 ,
  88 + MINIMUM_VALUE= 1,
  89 + MAXIMUM_VALUE= 1,
  90 + NUMBER_VALID= 15,
  91 + NUMBER_NULL= 0,
  92 +PARAMETER_HEADER,
  93 + TYPE= 'DOUB',
  94 + NAME= 'Sea Temperature (IPTS-68)',
  95 + UNITS= 'degrees C',
  96 + CODE= 'TEMP_01',
  97 + NULL_VALUE= -9.900000E+001,
  98 + PRINT_FIELD_WIDTH= 10,
  99 + PRINT_DECIMAL_PLACES= 3,
  100 + ANGLE_OF_SECTION= 0.000000,
  101 + MAGNETIC_VARIATION= 0.000000,
  102 + DEPTH= 0.000000 ,
  103 + MINIMUM_VALUE= 0.5,
  104 + MAXIMUM_VALUE= 4.1,
  105 + NUMBER_VALID= 15,
  106 + NUMBER_NULL= 0,
  107 +PARAMETER_HEADER,
  108 + TYPE= 'DOUB',
  109 + NAME= 'Quality flag: Sea Temperature (IPTS-68)',
  110 + UNITS= 'none',
  111 + CODE= 'QQQQ_02',
  112 + NULL_VALUE= -9.900000E+001,
  113 + PRINT_FIELD_WIDTH= 1,
  114 + PRINT_DECIMAL_PLACES= 0,
  115 + ANGLE_OF_SECTION= 0.000000,
  116 + MAGNETIC_VARIATION= 0.000000,
  117 + DEPTH= 0.000000 ,
  118 + MINIMUM_VALUE= 1,
  119 + MAXIMUM_VALUE= 1,
  120 + NUMBER_VALID= 15,
  121 + NUMBER_NULL= 0,
  122 +PARAMETER_HEADER,
  123 + TYPE= 'DOUB',
  124 + NAME= 'Quality flag: QCFF',
  125 + UNITS= 'none',
  126 + CODE= 'QCFF_01',
  127 + NULL_VALUE= -9.900000E+001,
  128 + PRINT_FIELD_WIDTH= 1,
  129 + PRINT_DECIMAL_PLACES= 0,
  130 + ANGLE_OF_SECTION= 0.000000,
  131 + MAGNETIC_VARIATION= 0.000000,
  132 + DEPTH= 0.000000 ,
  133 + MINIMUM_VALUE= 0,
  134 + MAXIMUM_VALUE= 0,
  135 + NUMBER_VALID= 15,
  136 + NUMBER_NULL= 0,
  137 +RECORD_HEADER,
  138 + NUM_CALIBRATION= 0,
  139 + NUM_SWING= 0,
  140 + NUM_HISTORY= 1,
  141 + NUM_CYCLE= 15,
  142 + NUM_PARAM= 5,
  143 + -- DATA --
  144 + 0.00 1 4.100 1 0
  145 + 0.40 1 4.000 1 0
  146 + 0.60 1 3.700 1 0
  147 + 1.30 1 3.400 1 0
  148 + 7.30 1 3.100 1 0
  149 + 13.50 1 3.200 1 0
  150 + 16.20 1 3.400 1 0
  151 + 22.30 1 3.300 1 0
  152 + 30.20 1 2.400 1 0
  153 + 35.40 1 1.900 1 0
  154 + 40.60 1 1.300 1 0
  155 + 50.00 1 0.900 1 0
  156 + 61.70 1 0.700 1 0
  157 + 76.10 1 0.600 1 0
  158 + 124.30 1 0.500 1 0
... ...
test_data/BT_1979005_1_1_.ODF 0 → 100644
... ... @@ -0,0 +1,150 @@
  1 +ODF_HEADER,
  2 + FILE_SPECIFICATION = 'BT_1979005_1_1_',
  3 +CRUISE_HEADER,
  4 + COUNTRY_INSTITUTE_CODE = 1830,
  5 + CRUISE_NUMBER = '1979005',
  6 + ORGANIZATION = 'DSO',
  7 + CHIEF_SCIENTIST = 'Jean-Claude Therriault',
  8 + START_DATE = '25-OCT-1979 00:00:00.00',
  9 + END_DATE = '03-NOV-1979 23:59:00.00',
  10 + PLATFORM = 'Unknown',
  11 + CRUISE_NAME = 'Mission 5, Octobre 1979, Variations saisonnieres de l`estuaire maritime',
  12 + CRUISE_DESCRIPTION = 'MEDS cruise number 1979005',
  13 +EVENT_HEADER,
  14 + DATA_TYPE= 'BT',
  15 + EVENT_NUMBER= '1',
  16 + EVENT_QUALIFIER1= '1',
  17 + EVENT_QUALIFIER2= '',
  18 + CREATION_DATE= '27-APR-2005 12:56:03.80',
  19 + ORIG_CREATION_DATE= '17-NOV-1858 00:00:00.00',
  20 + START_DATE_TIME= '02-NOV-1979 13:20:00.00',
  21 + END_DATE_TIME= '17-NOV-1858 00:00:00.00',
  22 + INITIAL_LATITUDE= 48.166700,
  23 + INITIAL_LONGITUDE= -69.566700,
  24 + END_LATITUDE= -99.990000,
  25 + END_LONGITUDE= -999.990000,
  26 + MIN_DEPTH= 0.00,
  27 + MAX_DEPTH= 137.90,
  28 + SAMPLING_INTERVAL= -99.0,
  29 + SOUNDING= 173.00,
  30 + DEPTH_OFF_BOTTOM= 35.10,
  31 + EVENT_COMMENTS= 'MEDS event number 34',
  32 + EVENT_COMMENTS= 'La profondeur du fond est celle à la position visée',
  33 +INSTRUMENT_HEADER,
  34 + INST_TYPE= 'Bathythermograph',
  35 + MODEL= 'Manual',
  36 + SERIAL_NUMBER= '',
  37 + DESCRIPTION= '',
  38 +QUALITY_HEADER,
  39 + QUALITY_DATE= '27-APR-2005 12:55:54.73',
  40 + QUALITY_TESTS= 'Test 1.1: GTSPP Platform Identification',
  41 + QUALITY_TESTS= 'Test 1.2: GTSPP Impossible Date/Time',
  42 + QUALITY_TESTS= 'Test 1.3: GTSPP Impossible Location',
  43 + QUALITY_TESTS= 'Test 1.4: GTSPP Position on Land',
  44 + QUALITY_TESTS= 'Test 1.5: GTSPP Impossible Speed',
  45 + QUALITY_TESTS= 'Test 1.6: GTSPP Impossible Sounding',
  46 + QUALITY_TESTS= 'Test 5.1: GTSPP Cruise Track Visual Inspection',
  47 + QUALITY_TESTS= 'Test 2.0: IML Minimum Descent Rate (2) (0.10m/s)',
  48 + QUALITY_TESTS= 'Test 2.1: GTSPP Global Impossible Parameter Values (4)',
  49 + QUALITY_TESTS= 'Test 2.2: GTSPP Regional Impossible Parameter Values (8)',
  50 + QUALITY_TESTS= 'Test 2.3: GTSPP Increasing Depth (16)',
  51 + QUALITY_TESTS= 'Test 2.4: GTSPP Profile Envelope (Temperature and Salinity) (32)',
  52 + QUALITY_TESTS= 'Test 2.7: GTSPP Spike in Temperature and Salinity (one point) (256)',
  53 + QUALITY_TESTS= 'Test 2.8: GTSPP Top and Bottom Spike in Temperature and Salinity (512)',
  54 + QUALITY_TESTS= 'Test 2.9: GTSPP Gradient in Temperature and Salinity (1024)',
  55 + QUALITY_TESTS= 'Test 3.5: IML Petrie Monthly Climatology (Temperature, Salinity and Sigma-T)',
  56 + QUALITY_TESTS= 'Test 5.2: GTSPP Profile Visual Inspection',
  57 + QUALITY_COMMENTS= 'QCFF values are derived from tests of GTSPP stage 2',
  58 + QUALITY_COMMENTS= 'A quality flag modified by hand has a QCFF value of 1',
  59 +HISTORY_HEADER,
  60 + CREATION_DATE= '13-APR-2005 15:46:45.97',
  61 + PROCESS= 'Conversion à partir d`un fichier MBT en provenance du MEDS',
  62 +PARAMETER_HEADER,
  63 + TYPE= 'DOUB',
  64 + NAME= 'Sensor pressure below Sea Surface',
  65 + UNITS= 'db',
  66 + CODE= 'PRES_01',
  67 + NULL_VALUE= -9.900000E+001,
  68 + PRINT_FIELD_WIDTH= 9,
  69 + PRINT_DECIMAL_PLACES= 2,
  70 + ANGLE_OF_SECTION= 0.000000,
  71 + MAGNETIC_VARIATION= 0.000000,
  72 + DEPTH= 0.000000 ,
  73 + MINIMUM_VALUE= 0,
  74 + MAXIMUM_VALUE= 137.9,
  75 + NUMBER_VALID= 7,
  76 + NUMBER_NULL= 0,
  77 +PARAMETER_HEADER,
  78 + TYPE= 'DOUB',
  79 + NAME= 'Quality flag: Sensor Depth below Sea Surface',
  80 + UNITS= 'none',
  81 + CODE= 'QQQQ_01',
  82 + NULL_VALUE= -9.900000E+001,
  83 + PRINT_FIELD_WIDTH= 1,
  84 + PRINT_DECIMAL_PLACES= 0,
  85 + ANGLE_OF_SECTION= 0.000000,
  86 + MAGNETIC_VARIATION= 0.000000,
  87 + DEPTH= 0.000000 ,
  88 + MINIMUM_VALUE= 1,
  89 + MAXIMUM_VALUE= 1,
  90 + NUMBER_VALID= 7,
  91 + NUMBER_NULL= 0,
  92 +PARAMETER_HEADER,
  93 + TYPE= 'DOUB',
  94 + NAME= 'Sea Temperature (IPTS-68)',
  95 + UNITS= 'degrees C',
  96 + CODE= 'TEMP_01',
  97 + NULL_VALUE= -9.900000E+001,
  98 + PRINT_FIELD_WIDTH= 10,
  99 + PRINT_DECIMAL_PLACES= 3,
  100 + ANGLE_OF_SECTION= 0.000000,
  101 + MAGNETIC_VARIATION= 0.000000,
  102 + DEPTH= 0.000000 ,
  103 + MINIMUM_VALUE= 1.3,
  104 + MAXIMUM_VALUE= 3.7,
  105 + NUMBER_VALID= 7,
  106 + NUMBER_NULL= 0,
  107 +PARAMETER_HEADER,
  108 + TYPE= 'DOUB',
  109 + NAME= 'Quality flag: Sea Temperature (IPTS-68)',
  110 + UNITS= 'none',
  111 + CODE= 'QQQQ_02',
  112 + NULL_VALUE= -9.900000E+001,
  113 + PRINT_FIELD_WIDTH= 1,
  114 + PRINT_DECIMAL_PLACES= 0,
  115 + ANGLE_OF_SECTION= 0.000000,
  116 + MAGNETIC_VARIATION= 0.000000,
  117 + DEPTH= 0.000000 ,
  118 + MINIMUM_VALUE= 1,
  119 + MAXIMUM_VALUE= 1,
  120 + NUMBER_VALID= 7,
  121 + NUMBER_NULL= 0,
  122 +PARAMETER_HEADER,
  123 + TYPE= 'DOUB',
  124 + NAME= 'Quality flag: QCFF',
  125 + UNITS= 'none',
  126 + CODE= 'QCFF_01',
  127 + NULL_VALUE= -9.900000E+001,
  128 + PRINT_FIELD_WIDTH= 1,
  129 + PRINT_DECIMAL_PLACES= 0,
  130 + ANGLE_OF_SECTION= 0.000000,
  131 + MAGNETIC_VARIATION= 0.000000,
  132 + DEPTH= 0.000000 ,
  133 + MINIMUM_VALUE= 0,
  134 + MAXIMUM_VALUE= 0,
  135 + NUMBER_VALID= 7,
  136 + NUMBER_NULL= 0,
  137 +RECORD_HEADER,
  138 + NUM_CALIBRATION= 0,
  139 + NUM_SWING= 0,
  140 + NUM_HISTORY= 1,
  141 + NUM_CYCLE= 7,
  142 + NUM_PARAM= 5,
  143 + -- DATA --
  144 + 0.00 3 3.700 1 0
  145 + 13.60 4 3.500 1 0
  146 + 21.50 1 3.100 1 0
  147 + 31.20 1 2.600 3 0
  148 + 56.90 1 1.800 4 0
  149 + 130.10 1 1.400 1 0
  150 + 137.90 1 1.300 1 0
... ...
test_data/BT_1979005_5_1_.ODF 0 → 100644
... ... @@ -0,0 +1,157 @@
  1 +ODF_HEADER,
  2 + FILE_SPECIFICATION = 'BT_1979005_5_1_',
  3 +CRUISE_HEADER,
  4 + COUNTRY_INSTITUTE_CODE = 1830,
  5 + CRUISE_NUMBER = '1979005',
  6 + ORGANIZATION = 'DSO',
  7 + CHIEF_SCIENTIST = 'Jean-Claude Therriault',
  8 + START_DATE = '25-OCT-1979 00:00:00.00',
  9 + END_DATE = '03-NOV-1979 23:59:00.00',
  10 + PLATFORM = 'Unknown',
  11 + CRUISE_NAME = 'Mission 5, Octobre 1979, Variations saisonnieres de l`estuaire maritime',
  12 + CRUISE_DESCRIPTION = 'MEDS cruise number 1979005',
  13 +EVENT_HEADER,
  14 + DATA_TYPE= 'BT',
  15 + EVENT_NUMBER= '5',
  16 + EVENT_QUALIFIER1= '1',
  17 + EVENT_QUALIFIER2= '',
  18 + CREATION_DATE= '27-APR-2005 12:56:03.69',
  19 + ORIG_CREATION_DATE= '17-NOV-1858 00:00:00.00',
  20 + START_DATE_TIME= '01-NOV-1979 19:44:00.00',
  21 + END_DATE_TIME= '17-NOV-1858 00:00:00.00',
  22 + INITIAL_LATITUDE= 48.266700,
  23 + INITIAL_LONGITUDE= -69.283300,
  24 + END_LATITUDE= -99.990000,
  25 + END_LONGITUDE= -999.990000,
  26 + MIN_DEPTH= 0.00,
  27 + MAX_DEPTH= 274.50,
  28 + SAMPLING_INTERVAL= -99.0,
  29 + SOUNDING= -99.00,
  30 + DEPTH_OFF_BOTTOM= -99.00,
  31 + EVENT_COMMENTS= 'MEDS event number 33',
  32 + EVENT_COMMENTS= 'Position ajustée en fonction de l`information inscrite sur la feuille d`échantillonnage',
  33 +INSTRUMENT_HEADER,
  34 + INST_TYPE= 'Bathythermograph',
  35 + MODEL= 'Manual',
  36 + SERIAL_NUMBER= '',